distui: Because Configuring GoReleaser Every Time is Fucking Tedious
Built a TUI to stop wasting 30 minutes configuring release pipelines for every new Go project
distui: Because Configuring GoReleaser Every Time is Fucking Tedious
Every time I build a new Go TUI (which is basically every week at this point), I waste 30 minutes setting up the same release configuration. GoReleaser config, GitHub Actions, Homebrew formula updates - it's the same shit every time.
The Problem: Release Configuration Groundhog Day
Here's what happens with every new Go project:
- Build something that works locally
- Want to distribute it
- Spend 30 minutes writing
.goreleaser.yaml
- Debug why the GitHub Action isn't triggering
- Fix the Homebrew tap formula path
- Realize you forgot to update the binary name in 3 places
- Push v0.1.1 because v0.1.0 is broken
The worst part? You can't just copy-paste from your last project because paths, binary names, and distribution channels are different.
The Solution: A TUI That Handles This Shit
Built distui
- a terminal UI that detects your Go project and generates all the release configuration you need.
cd your-go-project
distui
It detects:
- Your module name from
go.mod
- Git remote for GitHub integration
- Existing Homebrew taps (if you have any)
- Previous releases and versions
Then generates:
.goreleaser.yaml
with your actual binary name- Homebrew formula
- package.json
Implementation Details
The detection logic scans common directories:
func detectHomebrewTap(username string) string {
// Check if user has a homebrew tap
cmd := exec.Command("gh", "repo", "list",
"--json", "name", "--limit", "100")
output, _ := cmd.Output()
// Look for homebrew-* repos
repos := parseJSON(output)
for _, repo := range repos {
if strings.HasPrefix(repo.Name, "homebrew-") {
return fmt.Sprintf("%s/%s", username, repo.Name)
}
}
return ""
}
The key insight: don't store configs in the user's repo. Everything lives in ~/.distui/
:
~/.distui/
�� config.yaml # Global settings
�� projects/ # Per-project configs
�� myapp.yaml
�� templates/ # Release templates
This means zero repo pollution. Your project stays clean.
The LinkedIn Thread That Started This
Someone asked about best practices for Go distribution. I mentioned I was building a TUI for it. The responses were basically "just read the docs" and "use GitHub Actions."
Yeah, I know the docs exist. That's not the point. The point is automation. Why manually configure what a computer can figure out?
The real questions that came up:
- How to detect existing Homebrew formulas
- Whether to modify
.goreleaser.yaml
in place - How to handle custom build scripts
Current State
It works. Ships releases in 30 seconds instead of 30 minutes.
What's broken:
- Changelog generation (goreleaser v2 changed the syntax)
- Complex Git workflows (merge conflicts probably explode)
- Only supports Go (not touching other languages)
What works:
- Basic releases to GitHub
- Homebrew tap updates
- Version bumping (patch/minor/major)
- Branch cleanup
Installation
brew install willyv/tap/distui
Or if you want to build it:
go install github.com/willyv/distui@latest
The Architecture Decision
Used Bubble Tea for the TUI because it's the only Go framework that doesn't fight you. The entire state machine fits in 300 lines.
Key decision: direct command execution. No shell scripts, no temporary files. Just exec.Command()
straight to goreleaser
and gh
.
func executeRelease(version string) error {
cmd := exec.Command("goreleaser", "release",
"--clean", "--rm-dist")
cmd.Env = append(os.Environ(),
fmt.Sprintf("VERSION=%s", version))
return cmd.Run()
}
Lessons Learned
- Auto-detection beats configuration every time
- Keep generated files out of the repo
- ASCII art makes everything better (seriously, added a startup animation)
- First-time setup flow is critical - if it fails, users never come back
- "Custom mode" for people who already have configs is mandatory
The honest truth? Built this for myself. Got tired of the release configuration dance. If it helps someone else, cool. If not, at least my workflow is faster.
Next time I start a new TUI project (probably next week), it'll take 30 seconds to set up releases instead of 30 minutes. That's the whole point.