go-homedir

A tiny, cgo-free Go library for detecting and expanding the current user's home directory across platforms.

Library
Go
vv1.1.0
1,419stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
34/100Needs Attention
Development Activity0
Maintenance0
Community48
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
55/100Fair
Architecture80
Code Quality60
Innovation45
Learning Curve35

go-homedir is a small, dependency-free Go library that solves one specific problem: reliably locating the current user’s home directory without invoking cgo. The built-in os/user package requires cgo on Darwin, which breaks cross-compilation for Go programs that target other platforms from a single build machine. go-homedir sidesteps this by checking the HOME environment variable first, then falling back to platform-specific lookups (dscl on macOS, getent passwd on Linux/Unix, environment variables on Windows) and finally a shell fallback, all without a single cgo call.

The library exposes just two functions in practice: Dir() to get the home directory and Expand() to resolve a leading ~ in a path to that directory. Results are cached in memory by default (toggleable via DisableCache) and can be cleared with Reset() for testing. It has no external dependencies and is widely vendored as a transitive dependency across the Go ecosystem, including HashiCorp tooling.

What You Get

  • Dir() — returns the current user’s home directory using OS-appropriate detection, without cgo
  • Expand() — resolves a leading ~ (or ~/...) in a path string to the full home directory path
  • DisableCache and Reset() — control and clear the in-memory home-directory cache, useful for tests that swap $HOME
  • Cross-platform fallbacks — layered detection order (env var, OS-native lookup, shell fallback) for Unix-like systems, Windows, and Plan 9

Common Use Cases

  • CLI tools that accept file paths with a leading ~ and need to expand them the way a shell would
  • Cross-compiled Go binaries (e.g. built for multiple OS/arch targets from one CI machine) that can’t rely on cgo-dependent os/user
  • Locating a user-level config or cache directory (e.g. ~/.myapp/config.yaml) without shelling out manually
  • Testing code that depends on the home directory, using DisableCache/Reset() to simulate a different $HOME per test case

Under The Hood

Architecture The package is a single flat file exposing two public functions, Dir() and Expand(), plus a Reset() helper, backed by an unexported OS-dispatch layer (dirUnix, dirWindows) that branches at runtime on runtime.GOOS rather than using Go build tags, and a package-level result cache guarded by a sync.RWMutex (cacheLock) that can be disabled via the exported DisableCache flag. Dir() checks the cache, then dispatches to dirUnix() (tries $HOME, falls back to dscl on Darwin or getent passwd elsewhere, then a final shell fallback of sh -c "cd && pwd") or dirWindows() (checks $HOME, then $USERPROFILE, then $HOMEDRIVE+$HOMEPATH), caching whatever it finds. Expand() layers ~-prefix path logic on top of Dir(). There is no further layering — it’s a flat, single-purpose utility module, so any change to the core cache/dispatch logic in Dir() ripples directly into every consumer of Expand() and the package itself.

Tech Stack Pure Go standard library, no external dependencies — go.mod declares only the module path with no require entries. It uses os, os/exec (to shell out to dscl/getent/sh), path/filepath, runtime, strconv, strings, sync, bytes, and errors. There’s no build system beyond go build/go test, no ORM, web framework, or database involved — this is a general-purpose library meant to be imported by other Go programs, not a standalone deployable.

Code Quality A single test file (homedir_test.go) provides a benchmark (BenchmarkDir) and table-driven-style tests (TestDir, TestExpand) covering the core happy paths plus one error case (an ambiguous ~foo/foo expansion). Testing relies solely on the standard testing package — no assertion library, no mocking. Error handling is explicit and idiomatic (errors.New, propagated returns) rather than swallowed, and naming follows conventional Go style (exported Dir/Expand/Reset, unexported dirUnix/dirWindows). Coverage is present but narrow: the Darwin dscl and Windows-specific code paths aren’t exercised outside whichever OS the tests happen to run on, and no linter or CI configuration is visible in the repository.

What Makes It Unique go-homedir doesn’t introduce a novel algorithm — home-directory detection is a well-understood problem — but its specific value is enabling fully cgo-free cross-compilation: the standard library’s os/user package requires cgo on Darwin, which breaks single-machine cross-compilation for Go binaries targeting multiple platforms. go-homedir reimplements just enough of that lookup logic (environment variables, dscl/getent shell-outs, shell fallback) to avoid the cgo dependency entirely, which is why it became a widely vendored transitive dependency in cross-compiled Go tooling.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search