diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef4f4e4 --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# gbld โ€” Simple Go Builder & Live-Reloader + +`gbld` is a hot-reloader and build-automation utility for Go projects. It automatically tracks build numbers, formats source files using `goimports`, compiles the binary, manages the running process, and watches the project directory for changes to trigger automatic rebuilding and restarting. + +--- + +## Key Features + +- ๐Ÿ”„ **Hot Reloader**: Automatically watches for file changes (via `fsnotify`) and rebuilds/restarts your application instantly. +- ๐Ÿ”ข **Build Counter**: Increments a build number in `build.go` automatically with every compiled build. +- ๐Ÿงน **Auto-Import & Formatting**: Runs `goimports` on save (optional via `-i`) to keep your imports and formatting clean. +- ๐Ÿ–ฅ๏ธ **Interactive Menu**: Uses a select menu (`gbld.menu.json`) if run without arguments, letting you define and run custom build tasks. +- ๐Ÿš€ **Cross-Compilation**: Easily builds binaries for multiple target operating systems (macOS, Linux, Windows) and architectures (amd64, arm64) using a single command. +- โ˜๏ธ **Remote Uploads**: Integrated command to securely upload build artifacts to a deployment server (`gozilla`). + +--- + +## Installation + +### Prerequisites +Make sure you have [Go](https://go.dev/) and `goimports` installed and available in your system path: +```bash +go install golang.org/x/tools/cmd/goimports@latest +``` + +### Building from Source +Clone the repository and compile `gbld`: +```bash +git clone https://git.fhi.mpg.de/mike/gbld.git +cd gbld +go build -o /usr/local/bin/gbld +``` + +--- + +## Usage + +```bash +gbld [flags] [target_name] +``` +> [!NOTE] +> If `target_name` is omitted, the name of the current directory is used. The tool expects a main source file matching that name (e.g., `target_name.go`). + +### CLI Flags + +| Flag | Argument | Description | +| :--- | :--- | :--- | +| `-b` | None | **Build-only** mode (compiles the binary but does not run it). | +| `-1` | None | Run **once** (does not watch the directory for changes). | +| `-i` | None | Runs `goimports` to clean up imports before building. | +| `-o` | `` | Space-separated arguments to pass to the compiled binary on execution. | +| `-c` | `` | Command to execute immediately after a successful build (before execution). | +| `-a` | None | Builds binaries for all supported platforms (Windows, macOS, Linux for AMD64/ARM64). | +| `-u` | None | Uploads built binaries via SSH/SCP to the `gozilla` server. | +| `-v` | None | Displays the current version of `gbld`. | +| `-h` | None | Displays help and usage information. | + +--- + +## Configuration (`gbld.menu.json`) + +To make running common tasks easier, you can place a `gbld.menu.json` file in your project directory. If you run `gbld` without any flags, it will parse this file and present an interactive selection menu: + +```json +{ + "Build and Run (hot reload)": ["-i"], + "Build Only": ["-b", "-i", "-1"], + "Show Help": ["-h"] +} +``` + +--- + +## Under the Hood + +### Build Increment +When compiling, `gbld` parses `build.go` to find a pattern matching `var build = ""`. It increments this number by 1 and updates the file automatically. This is useful for stamping build numbers/versions inside your binary. + +### Automatic Backups +Before `gbld` runs `goimports` on a file or modifies `build.go`, it creates a timestamped backup in a `./tmp/` directory inside the project workspace: +``` +./tmp/gbld.go.20260625145800 +``` + +### Process Management +When hot-reloading, `gbld` handles process termination gracefully: +1. It sends an `os.Interrupt` (SIGINT) to the running binary. +2. It waits up to 1 second for the process to exit clean. +3. If it does not exit within the timeout, it terminates the process forcefully (`SIGKILL`). diff --git a/build.go b/build.go index 46d30d4..b7f6f5f 100644 --- a/build.go +++ b/build.go @@ -1,3 +1,3 @@ package main -var build = "354" +var build = "362" diff --git a/gbld.go b/gbld.go index e94e928..9ac90ae 100644 --- a/gbld.go +++ b/gbld.go @@ -16,9 +16,10 @@ import ( "time" "github.com/AlecAivazis/survey/v2" + "github.com/fsnotify/fsnotify" ) -var version = "1.19.6" +var version = "1.20.1" var UPDATEURL = "http://gozilla.fhi.mpg.de/gbld" @@ -27,9 +28,14 @@ var arches = []string{"arm64", "amd64"} var GO string var GOIMPORTS string +var xmd *exec.Cmd var done chan bool -var r1, r2, r3 *regexp.Regexp +var r1 = regexp.MustCompile(`^\.`) +var r2 = regexp.MustCompile(`build\.go`) +var r3 = regexp.MustCompile(`\.go$`) +var r4 = regexp.MustCompile(`var\s+build\s*=\s*"(.*)"$`) +var r5 = regexp.MustCompile(`(?i)var\s+version\s*=\s*"(.*)"$`) func main() { // ========================================================================================== main @@ -52,7 +58,7 @@ func main() { // =============================================================== os.Args = append(os.Args, buildjson[tmp]...) } else { PE(err.Error()) - os.Exit(0); + os.Exit(1) } } else { os.Args = []string{"gbld","-b","-i","-1",dirName()} @@ -105,21 +111,18 @@ func main() { // =============================================================== path, err := exec.LookPath("go") // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท check for 'go' if err != nil { fmt.Println(Crb("Error: ") + Cwb("go") + " not found") - os.Exit(0) - + os.Exit(1) } GO = path - path, err = exec.LookPath("goimports") // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท check for 'goimports' if err != nil { fmt.Println(Crb("Error: ") + Cwb("goimports") + " not found") - os.Exit(0) + os.Exit(1) } GOIMPORTS = path - - if *opt_v { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท show version + if *opt_v { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท show version info() os.Exit(0) } @@ -134,31 +137,26 @@ func main() { // =============================================================== name=dirName() if len(name) == 0 { flag.Usage() - os.Exit(0) + os.Exit(1) } } file := name + ".go" if !fileExists(file) { P(Crb("file not found: " + file)) - os.Exit(0) + os.Exit(1) } - var ot int64 = 0 - var st int = 1 - var xmd *exec.Cmd = nil - if (!*opt_1 && !*opt_a && !*opt_u) { PN("\033[2J\033[1;1H") } - info() + info() if *opt_a { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท build for other platforms - targetversion:=gettargetversion(file) + targetversion:=gettargetversion() P(Ccb("build for all platforms")) for _, osys := range oses { for _, arch := range arches { - ofile:="bin/"+name+"_"+targetversion+"_"+osys+"_"+arch P(Cy(ofile)) @@ -167,13 +165,11 @@ func main() { // =============================================================== cmd.Run() } } - os.WriteFile("bin/version.txt", []byte(targetversion), 0644) - } if *opt_u { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท upload builds to gozilla - targetversion:=gettargetversion(file) + targetversion:=gettargetversion() P(Ccb("upload builds to gozilla")) for _, osys := range oses { @@ -181,12 +177,10 @@ func main() { // =============================================================== ofile:="bin/"+name+"_"+targetversion+"_"+osys+"_"+arch _, err := os.Stat(ofile) if err == nil { - cmd := exec.Command("ssh","root@gozilla","rm", "/db/www/"+name+"/*_"+osys+"_"+arch ) P(Cy(cmd)) cmd.Run() - cmd = exec.Command("scp",ofile, "root@gozilla:/db/www/"+name) P(Cy(cmd)) cmd.Run() @@ -199,168 +193,171 @@ func main() { // =============================================================== P(Cy(cmd)) cmd.Run() } - } - if (*opt_a || *opt_u) { os.Exit(0) } - - r1 = regexp.MustCompile("^\\.") - r2 = regexp.MustCompile("build\\.go") - r3 = regexp.MustCompile("\\.go$") - for { // ------------------------------------------------------------------------------------------- main loop - lmf := lmf("./") + // Initial build + buildAndRun(name, file, *opt_b, *opt_1, *opt_i, *opt_c, *opt_o, *opt_x) - var dt int64 = 0 + if *opt_1 { + os.Exit(0) + } - if fileExists(name) { - dt = lmf - filemodtime(name) - } else { - dt = 1 + // Set up fsnotify watcher + watcher, err := fsnotify.NewWatcher() + if err != nil { + PE(err.Error()) + os.Exit(1) + } + defer watcher.Close() + + err = watcher.Add(".") + if err != nil { + PE(err.Error()) + os.Exit(1) + } + + events := make(chan bool) + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { + fileName := filepath.Base(event.Name) + if r3.MatchString(fileName) && !r1.MatchString(fileName) && !r2.MatchString(fileName) { + events <- true + } + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + PE(err.Error()) + } } + }() - if (dt > 0 && ot != lmf) || st == 1 { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท new version detected - - if st == 0 && !*opt_1{ - PN("\033[2J\033[1;1H") + var timer *time.Timer + for { + select { + case <-events: + if timer != nil { + timer.Stop() } - P(Cc("--- start compiling " + file + " ---")) - - stopRunning(xmd) - - o := "" // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท increase build number - - readFile, err := os.Open("build.go") - if err == nil { - defer readFile.Close() - backup("build.go") - fileScanner := bufio.NewScanner(readFile) - fileScanner.Split(bufio.ScanLines) - r := regexp.MustCompile("var\\s+build\\s*=\\s*\"(.*)\"$") - for fileScanner.Scan() { - line := fileScanner.Text() - s := r.FindStringSubmatch(line) - if len(s) > 0 { - b := Atoi(s[1]) + 1 - o = o + "var build = \"" + Itoa(b) + "\"\n" - P("build:", s[1], "->", b) - } else { - o = o + line + "\n" - } - } - os.WriteFile("build.go", []byte(o), 0644) - } - - targetversion:="" - readFile, err = os.Open(file) - if err == nil { - fileScanner := bufio.NewScanner(readFile) - fileScanner.Split(bufio.ScanLines) - r := regexp.MustCompile("var\\s+version\\s*=\\s*\"(.*)\"$") - for fileScanner.Scan() { - line := fileScanner.Text() - s := r.FindStringSubmatch(line) - if len(s) > 0 { - targetversion=s[1] - } - } - readFile.Close() - } - - if (targetversion=="") { - PE("target version not found") - os.Exit(0) - } - - if *opt_i { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท run goimports - re := regexp.MustCompile(`(?s)import\s*\((.*?)\)`) - files, err := os.ReadDir(".") - if err != nil { fmt.Printf("Error: %v\n", err); return } - - for _, f := range files { - if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") { - fileName := f.Name() - cmd := exec.Command("goimports", fileName) - output, err := cmd.Output() - if err != nil { continue } - origContent, _ := os.ReadFile(fileName) - tempMatch := re.FindStringSubmatch(string(output)) - if len(tempMatch) < 2 { continue } - newImportBlock := tempMatch[1] - updatedContent := ReplaceFirst(re,string(origContent),fmt.Sprintf("import (%s)", newImportBlock)) - if (string(origContent) != updatedContent) { - backup(fileName) - PF("%s %s\n",Cw("imports updated:"),Cmb(fileName)) - os.WriteFile(fileName, []byte(updatedContent), 0644) - } - } - } - - } - - cmd := exec.Command("go", "build", "-o", "bin/"+name, name) - - PF("%s %s\n", Cw("running:"), Cyb("go build "+name)) - - out, err := cmd.CombinedOutput() - - if err != nil { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท build failed - - PN(Cy(string(out))) - P(Cr("--- build failed: " + name + " ---")) - - } else { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท build succeeded - - P(Cb("--- compiling done: " + name + " ---")) - - if len(*opt_c) > 0 { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท execute post command - params := strings.Fields(*opt_c) - app := params[0] - args := params[1:] - - P(Cw(*opt_c)) - xmd = exec.Command(app, args...) - xmd.Stdout = os.Stdout - xmd.Stdin = os.Stdin - xmd.Stderr = os.Stderr - xmd.Run() - } - - if !*opt_b && !*opt_a { - stopRunning(xmd) - - args := []string{} - if len(*opt_o) > 0 { - args = strings.Fields(*opt_o) - } - - xmd = exec.Command("./bin/"+name, args...) // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท start new build - var stdoutBuf, stderrBuf bytes.Buffer - xmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) - xmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) - xmd.Start() - - PF("%s %d\n", Cw("process started:"), xmd.Process.Pid) - } - - P(Cg("--- end building: " + name + " ---")) - if *opt_x { - os.Exit(0) - } - } - ot = lmf + timer = time.AfterFunc(100*time.Millisecond, func() { + buildAndRun(name, file, *opt_b, *opt_1, *opt_i, *opt_c, *opt_o, *opt_x) + }) } - - if *opt_1 { - os.Exit(0) - } - - st = 0 - time.Sleep(500 * time.Millisecond) } } +func buildAndRun(name, file string, opt_b, opt_1, opt_i bool, opt_c, opt_o string, opt_x bool) { + PN("\033[2J\033[1;1H") + P(Cc("--- start compiling " + file + " ---")) + + stopRunning(xmd) + + o := "" // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท increase build number + func() { + readFile, err := os.Open("build.go") + if err == nil { + defer readFile.Close() + backup("build.go") + fileScanner := bufio.NewScanner(readFile) + fileScanner.Split(bufio.ScanLines) + for fileScanner.Scan() { + line := fileScanner.Text() + s := r4.FindStringSubmatch(line) + if len(s) > 0 { + b := Atoi(s[1]) + 1 + o = o + "var build = \"" + Itoa(b) + "\"\n" + P("build:", s[1], "->", b) + } else { + o = o + line + "\n" + } + } + os.WriteFile("build.go", []byte(o), 0644) + } + }() + + _ = gettargetversion() + + if opt_i { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท run goimports + re := regexp.MustCompile(`(?s)import\s*\((.*?)\)`) + files, err := os.ReadDir(".") + if err != nil { fmt.Printf("Error: %v\n", err); return } + + for _, f := range files { + if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") { + fileName := f.Name() + cmd := exec.Command("goimports", fileName) + output, err := cmd.Output() + if err != nil { continue } + origContent, _ := os.ReadFile(fileName) + tempMatch := re.FindStringSubmatch(string(output)) + if len(tempMatch) < 2 { continue } + newImportBlock := tempMatch[1] + updatedContent := ReplaceFirst(re,string(origContent),fmt.Sprintf("import (%s)", newImportBlock)) + if (string(origContent) != updatedContent) { + backup(fileName) + PF("%s %s\n",Cw("imports updated:"),Cmb(fileName)) + os.WriteFile(fileName, []byte(updatedContent), 0644) + } + } + } + } + + cmd := exec.Command("go", "build", "-o", "bin/"+name, name) + PF("%s %s\n", Cw("running:"), Cyb("go build "+name)) + + out, err := cmd.CombinedOutput() + + if err != nil { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท build failed + PN(Cy(string(out))) + P(Cr("--- build failed: " + name + " ---")) + } else { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท build succeeded + P(Cb("--- compiling done: " + name + " ---")) + + if len(opt_c) > 0 { // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท execute post command + params := strings.Fields(opt_c) + app := params[0] + args := params[1:] + + P(Cw(opt_c)) + xmd = exec.Command(app, args...) + xmd.Stdout = os.Stdout + xmd.Stdin = os.Stdin + xmd.Stderr = os.Stderr + xmd.Run() + } + + if !opt_b { + stopRunning(xmd) + + args := []string{} + if len(opt_o) > 0 { + args = strings.Fields(opt_o) + } + + xmd = exec.Command("./bin/"+name, args...) // ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท start new build + var stdoutBuf, stderrBuf bytes.Buffer + xmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) + xmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) + xmd.Start() + + PF("%s %d\n", Cw("process started:"), xmd.Process.Pid) + } + + P(Cg("--- end building: " + name + " ---")) + if opt_x { + os.Exit(0) + } + } +} func backup(src string) { // ---------------------------------------------------------------------- backup files sourceFile,_ := os.Open(src) @@ -373,61 +370,61 @@ func backup(src string) { // --------------------------------------------------- destFile.Close() } -// ===================================================================================================== SUPPORT - -func gettargetversion(file string) string { // ------------------------------------------- detect target version - targetversion:="" - readFile, err := os.Open(file) - if err == nil { +func gettargetversion() string { // ------------------------------------------- detect target version + files, err := filepath.Glob("*.go") + if err != nil { + PE(SF("Error listing files: %v", err)) + os.Exit(1) + } + + targetversion := "" + for _, file := range files { + readFile, err := os.Open(file) + if err != nil { + continue + } fileScanner := bufio.NewScanner(readFile) fileScanner.Split(bufio.ScanLines) - r := regexp.MustCompile("var\\s+version\\s*=\\s*\"(.*)\"$") for fileScanner.Scan() { line := fileScanner.Text() - s := r.FindStringSubmatch(line) + s := r5.FindStringSubmatch(line) if len(s) > 0 { - targetversion=s[1] + targetversion = s[1] + readFile.Close() + return targetversion } } readFile.Close() } - if (targetversion=="") { + if targetversion == "" { PE("target version not found") - os.Exit(0) + os.Exit(1) } return targetversion } - -func lmf(dir string) int64 { // -------------------------------------- get last modification date from .go files - var tmax int64 = 0 - files, err := os.ReadDir(dir) - if err == nil { - for _, file := range files { - if r3.MatchString(file.Name()) { - if !r1.MatchString(file.Name()) && !r2.MatchString(file.Name()) { - tf := filemodtime(file.Name()) - if tf > tmax { - tmax = tf - } - } - } - } - } - return tmax -} - func stopRunning(cmd *exec.Cmd) { // -------------------------------------------------- stop running old process if cmd == nil || cmd.ProcessState != nil && cmd.ProcessState.Exited() || cmd.Process == nil { return } - err := cmd.Process.Kill() + err := cmd.Process.Signal(os.Interrupt) if err == nil { - cmd.Wait() + done := make(chan error, 1) + go func() { done <- cmd.Wait() }() + select { + case <-time.After(1 * time.Second): + cmd.Process.Kill() + <-done + case <-done: + } P("process stopped:", cmd.Process.Pid) + } else { + cmd.Process.Kill() + cmd.Wait() + P("process stopped forcefully:", cmd.Process.Pid) } } diff --git a/go.mod b/go.mod index 1f250cf..738cbf6 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,10 @@ go 1.26.1 require ( github.com/AlecAivazis/survey/v2 v2.3.7 + github.com/Masterminds/semver/v3 v3.5.0 github.com/eknkc/basex v1.0.1 github.com/fatih/color v1.19.0 + github.com/fsnotify/fsnotify v1.9.0 github.com/minio/selfupdate v0.6.0 github.com/spf13/viper v1.21.0 github.com/tidwall/gjson v1.19.0 @@ -13,8 +15,6 @@ require ( require ( aead.dev/minisign v0.2.0 // indirect - github.com/Masterminds/semver/v3 v3.5.0 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/mattn/go-colorable v0.1.14 // indirect diff --git a/linux.go b/linux.go deleted file mode 100644 index 16e82ab..0000000 --- a/linux.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build linux - -package main - -import ( - "os" - "syscall" -) - -func filemodtime(build string) int64 { - fibuild, _ := os.Stat(build) - tb := fibuild.Sys().(*syscall.Stat_t).Mtim - return tb.Sec -} diff --git a/mac.go b/mac.go deleted file mode 100644 index 90eda99..0000000 --- a/mac.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build darwin - -package main - -import ( - "os" - "syscall" -) - -func filemodtime(build string) int64 { - fibuild, _ := os.Stat(build) - tb := fibuild.Sys().(*syscall.Stat_t).Mtimespec - return tb.Sec -}