<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go CLI &amp; Tooling on Atharva Pandey</title><link>https://atharva.page/series/go-cli--tooling/</link><description>Recent content in Go CLI &amp; Tooling on Atharva Pandey</description><generator>Hugo</generator><language>en-us</language><copyright>Copyright ©</copyright><lastBuildDate>Thu, 05 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://atharva.page/series/go-cli--tooling/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 8: Packaging and Distributing — GoReleaser, Homebrew, and getting your tool to users</title><link>https://atharva.page/post/go/go-cli-distribution/</link><pubDate>Thu, 05 Jun 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-distribution/</guid><description>&lt;p&gt;Building a great CLI tool is half the job. The other half is getting it to users without making them compile it from source, navigate a GitHub releases page manually, or run a curl-pipe-to-bash script from an unverified URL. Distribution is where many Go projects stop short: the binary exists, the README says &lt;code&gt;go install&lt;/code&gt;, and that is considered &amp;ldquo;distributed.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go install&lt;/code&gt; is fine for Go developers. It is not acceptable for operators, system administrators, and end users who reasonably expect &lt;code&gt;brew install&lt;/code&gt; or &lt;code&gt;apt install&lt;/code&gt;. GoReleaser closes this gap by automating the full release pipeline — cross-compiled binaries, checksums, GitHub releases, Homebrew formulas, Debian packages, Docker images — from a single configuration file and one &lt;code&gt;git push --tags&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Lesson 7: Build Flags and ldflags — Inject version info at compile time</title><link>https://atharva.page/post/go/go-cli-build-flags/</link><pubDate>Sat, 05 Apr 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-build-flags/</guid><description>&lt;p&gt;A CLI tool that cannot tell you what version it is running is a frustrating tool to operate. When something breaks, the first question is &amp;ldquo;which version?&amp;rdquo; Without a proper answer, debugging becomes archaeology. The good news is that Go&amp;rsquo;s build system provides two mechanisms for injecting metadata at compile time — &lt;code&gt;ldflags&lt;/code&gt; for injecting variable values from the shell, and build constraints for including or excluding code based on the build context — and both are straightforward once you understand their syntax.&lt;/p&gt;</description></item><item><title>Lesson 6: Embedding Assets — embed.FS puts files inside your binary</title><link>https://atharva.page/post/go/go-cli-embed/</link><pubDate>Wed, 05 Feb 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-embed/</guid><description>&lt;p&gt;Before Go 1.16, embedding static assets in a Go binary required either a code generation tool that converted files to byte arrays, a third-party library like &lt;code&gt;packr&lt;/code&gt; or &lt;code&gt;statik&lt;/code&gt;, or shipping the files alongside the binary and reading them from disk at runtime. Each approach had real costs: generated code bloated repositories, third-party tools had to be installed separately, and shipping separate files broke the &amp;ldquo;single binary&amp;rdquo; distribution story.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;//go:embed&lt;/code&gt; directive in Go 1.16 solved this cleanly. Files, directories, and whole asset trees can be embedded directly into the binary with a single comment and a variable declaration. Templates, SQL migrations, web assets, default configs — everything your binary needs can travel with it.&lt;/p&gt;</description></item><item><title>Lesson 5: Cross-Compilation — Build for Linux from your Mac in one command</title><link>https://atharva.page/post/go/go-cli-cross-compile/</link><pubDate>Tue, 10 Dec 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-cross-compile/</guid><description>&lt;p&gt;One of Go&amp;rsquo;s most practical superpowers is that cross-compilation is a first-class feature, not an afterthought. Two environment variables — &lt;code&gt;GOOS&lt;/code&gt; and &lt;code&gt;GOARCH&lt;/code&gt; — are all you need to produce a Linux binary from macOS, a Windows executable from Linux, or an ARM binary from an x86 machine. No Docker containers required, no cross-compilation toolchain setup, no linker flags hunting. Just &lt;code&gt;GOOS=linux GOARCH=amd64 go build&lt;/code&gt; and you have a production-ready binary for the target platform.&lt;/p&gt;</description></item><item><title>Lesson 4: Signal Handling — Catch SIGTERM or lose your work</title><link>https://atharva.page/post/go/go-cli-signals/</link><pubDate>Wed, 30 Oct 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-signals/</guid><description>&lt;p&gt;Most CLI tools work perfectly on the happy path and fail silently on the unhappy one. The user presses Ctrl-C, the OS sends SIGINT, and the process dies immediately — leaving a temp file half-written, a database connection open, or a progress bar frozen mid-operation. The problem is invisible because most of the time nobody looks at what gets left behind. Until a deployment script depends on that temp file being complete, or a database hits its connection limit, or a batch job loses six hours of progress because the container was terminated between checkpoints.&lt;/p&gt;</description></item><item><title>Lesson 3: File I/O Patterns — Read, write, stream without loading everything into memory</title><link>https://atharva.page/post/go/go-cli-file-io/</link><pubDate>Thu, 12 Sep 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-file-io/</guid><description>&lt;p&gt;CLI tools spend most of their life doing file I/O. Reading config files, processing log dumps, writing output, transforming data from stdin to stdout — it all comes down to bytes moving through your program. The difference between a CLI tool that handles 100MB files gracefully and one that runs out of memory on large inputs is almost always whether you read everything into memory or stream it.&lt;/p&gt;
&lt;p&gt;Go&amp;rsquo;s standard library gives you everything you need to stream data efficiently. The key is knowing which functions to reach for and which ones to avoid when the input is large or unknown in size.&lt;/p&gt;</description></item><item><title>Lesson 2: Config and Env Handling — Viper, envconfig, or just os.Getenv?</title><link>https://atharva.page/post/go/go-cli-config/</link><pubDate>Mon, 05 Aug 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-config/</guid><description>&lt;p&gt;Configuration is one of those problems that looks trivial until it is not. A single environment variable is three lines of code. A configuration file with overridable environment variables, sensible defaults, validation, and reload-on-signal is a project in itself. Knowing when to use each approach — raw &lt;code&gt;os.Getenv&lt;/code&gt;, struct-based env decoding, or a full configuration library like Viper — is more about understanding the tradeoffs than about which library is &amp;ldquo;best.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 1: Building CLIs with Cobra — From main.go to production CLI</title><link>https://atharva.page/post/go/go-cli-cobra/</link><pubDate>Sat, 22 Jun 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-cli-cobra/</guid><description>&lt;p&gt;Every Go project that grows into something useful eventually needs a command-line interface. Maybe it starts as a quick &lt;code&gt;main.go&lt;/code&gt; with &lt;code&gt;os.Args[1]&lt;/code&gt; checks and a switch statement. That works until you need subcommands, flags, help text, shell completion, and version information — and suddenly you are maintaining a hand-rolled argument parser that nobody wants to touch. Cobra is the standard library-grade solution to this problem, and learning to structure a Cobra application properly saves you from rewriting it twice.&lt;/p&gt;</description></item></channel></rss>