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), recursively including subdirectories, and rebuilds/restarts your application instantly. - 🔢 Build Counter: Increments a build number in
build.goautomatically with every compiled build. - 🧹 Auto-Import: Runs
goimportson save (optional via-i) to keep your imports clean — without touching the rest of your formatting. - 🖥️ 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 upload build artifacts to a deployment server (
gozilla). - 🔐 Verified Self-Update: Downloads updates over HTTPS and refuses to install anything that does not match its published SHA-256 checksum.
Installation
Prerequisites
Make sure you have Go installed and available in your system path. goimports is only required if you use the -i flag:
go install golang.org/x/tools/cmd/goimports@latest
Building from Source
Clone the repository and compile gbld:
git clone https://git.fhi.mpg.de/mike/gbld.git
cd gbld
go build -o /usr/local/bin/gbld
Usage
gbld [flags] [target_name]
Note
If
target_nameis omitted, the name of the current directory is used. The tool expects a main source file matching that name (e.g.,target_name.go). The binary is always built from the package in the current directory, so the module path does not have to match the target name.
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), see Exit Codes. |
-i |
None | Runs goimports to clean up imports before building. |
-x |
None | Exits after the first successful build and leaves the started binary running. |
-o |
<args> |
Space-separated arguments to pass to the compiled binary on execution. |
-c |
<command> |
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. |
Environment
| Variable | Description |
|---|---|
GBLD_NO_UPDATE |
Set to any value to skip the update check at startup (useful offline or in CI). |
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 (entries are shown in alphabetical order):
{
"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 = "<number>". It increments this number by 1 and updates the file automatically, leaving the rest of the line (including trailing comments) untouched. This is useful for stamping build numbers/versions inside your binary. If there is no build.go, this step is silently skipped.
Target Version
-a and -u name their artifacts after the first var version = "x.y.z" found in the *.go files of the project. Ordinary builds do not need this variable.
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 (the directory is created on demand):
./tmp/gbld.go.20260625145800
If a backup cannot be written, the file is not modified.
File Watching
The project directory is watched recursively; directories created while gbld is running are picked up automatically. Hidden entries (.git, …) as well as bin, tmp, vendor, node_modules and testdata are skipped. Only writes and newly created .go files trigger a rebuild — a pure touch (mtime only) does not. Rebuilds are debounced by 100 ms and never run in parallel.
Process Management
When hot-reloading, gbld handles process termination gracefully:
- It sends an
os.Interrupt(SIGINT) to the running binary. - It waits up to 1 second for the process to exit clean.
- If it does not exit within the timeout, it terminates the process forcefully (
SIGKILL).
gbld itself also traps SIGINT/SIGTERM and stops the started binary before exiting, so no orphaned processes are left behind — except with -x, where detaching is the point.
Exit Codes
With -1, gbld is usable from scripts and CI:
| Situation | Exit code |
|---|---|
| Build failed | 1 |
-b -1, build succeeded |
0 |
-1 without -b |
exit code of the built program (gbld waits for it) |
Cross-Compilation and Upload
gbld -a # build bin/<name>_<version>_<os>_<arch> for all 6 targets
gbld -u # upload them to gozilla
-a writes Windows artifacts with an .exe suffix and additionally produces:
bin/checksums.txt— SHA-256 of every artifact, in the format used byshasum -a 256 -cbin/version.txt— the published version
If any target fails to build, version.txt is not written and gbld exits with 1, so a half-published version can never be announced. -u refuses build names containing anything but A-Za-z0-9._- and uploads the artifacts together with checksums.txt and version.txt.
Self-Update
On startup gbld fetches version.txt from the update server (3 s timeout — an unreachable server never blocks a build). If a newer version is published, it asks before downloading, then verifies the download against the SHA-256 from checksums.txt before replacing itself. If no matching checksum is published, the update is refused. Publish new versions with gbld -a followed by gbld -u.
Colors
Output uses the Catppuccin Mocha palette as 24-bit color:
| Helper | Palette | Hex |
|---|---|---|
Cr / Crb |
Red | #f38ba8 |
Cg / Cgb |
Green | #a6e3a1 |
Cy / Cyb |
Yellow | #f9e2af |
Cb / Cbb |
Blue | #89b4fa |
Cm / Cmb |
Pink | #f5c2e7 |
Cc / Ccb |
Teal | #94e2d5 |
Cw |
Subtext1 | #bac2de |
Cwb |
Text | #cdd6f4 |
Mocha maps identical values to the normal and bright ANSI slots, so the bold helpers keep their hue and only add weight. The full palette is available as CatMauve, CatPeach, CatLavender, … in tools.go.
The interactive prompts (survey) are themed as well: their templates resolve colors through {{color "green+hb"}}-style names, and tools.go replaces that template function with one that maps those names onto the palette. This covers every prompt including its icons — question mark, select arrow, help and error markers — without copying any template.
Terminals that do not announce 24-bit color via COLORTERM fall back to the basic ANSI colors, so their own theme keeps deciding. NO_COLOR and non-TTY output disable colors entirely.
Development
go test ./... # unit tests (build counter, version detection, update verification, toolbox)
go vet ./...
Note
The sources are deliberately not
gofmt-formatted (2-space indentation). The-iflag only replaces theimport (...)block of a file and leaves the rest of the formatting alone — do not rungofmtover the tree.