A fast, cross-platform Go version manager written in Rust.
Install, switch, and pin any Go release - no sudo, no system dependencies, no fuss.
Warning
Only get gvsn from this repository's official Releases page or the install commands below. Clones of this repository exist that distribute unofficial binaries under the same name. See SECURITY.md for known cases, the plain-language walkthrough of how one clone's malware works, or the full technical writeup of the active malware-distributing clone.
gvsn is a Go version manager built from scratch in Rust. It was designed with a single goal: work everywhere, require nothing.
- No Go required - you don't need Go installed to install Go. gvsn downloads the official toolchain directly from go.dev.
- No
sudo, no root - everything lives under~/.gvsnin your home directory. - Zero system dependencies - a single static binary is all you need on Windows, Linux (x86_64/ARM64/ARMv7/386), and macOS. The additional Linux server architectures (RISC-V, IBM Z, IBM POWER) link against the system's glibc instead, since Rust has no stable static (musl) target for them yet - glibc ships with essentially every Linux distribution and is backward-compatible, so this rarely matters in practice.
- Truly cross-platform - one codebase, one behavior across Windows, Linux, and macOS on both x86_64 and ARM64.
- SHA-256 verified downloads - every archive is checked against go.dev's official checksum before extraction.
- Fast, resumable downloads - a single stream with a large read buffer to use as much of the link's throughput as possible. Interrupted downloads resume automatically from the last byte.
- Transparent build output -
gvsn build -vstreams every compiler line in real time so you always know what is happening. - Session-scoped activation -
gvsn shell <version>activates a version for the current terminal only, without touching any files. - Full environment setup -
gvsn setupconfigures everything: shell hook, login profile PATH (so GUI apps like VSCode find Go), and Windows registry. Works correctly after a fresh install or a shell change. - Self-updating -
gvsn upgradedownloads and replaces the binary in-place. - Clean uninstall -
gvsn imploderemoves everything gvsn ever touched.
A fair question for a Go tool. A few honest reasons, not a verdict on Go:
- Memory safety for the riskiest operations.
gvsn upgradereplaces its own running binary,gvsn setupwrites to your shell profile and the Windows registry PATH, andgvsn implodedeletes files across your home directory. These are exactly the operations where a memory bug turns into a corrupted install or a broken shell - Rust's ownership model rules out that class of bug at compile time. - A mature, boring cross-compilation story. Twelve target platforms build from one codebase,
with static linking handled by
crossand musl/static-MSVC targets - no per-platform toolchain juggling to maintain. - Not depending on the exact ecosystem it manages. Both Go and Rust produce standalone static binaries, so "you don't need Go to install Go" isn't unique to Rust - but keeping this tool's own build independent of the toolchain it manages felt like the right instinct.
Plenty of excellent Go version managers are written in Go itself - this was a deliberate choice for this project, not a claim that Rust is categorically the right call for CLI tooling.
- π₯ Install any Go version - by exact version, minor range, or
latest - β‘ Fast, resumable downloads - single-stream with automatic resume on interruption (
--retries) - π¨ Build from source - compile any Go version from the official source tarball with automatic bootstrap detection and real-time streaming output
- π Global default - set a system-wide version with
gvsn use - π Per-project pinning - drop a
.go-versionfile; gvsn activates it automatically - π Session-scoped activation -
gvsn shell <version>switches versions for the current terminal only, no files written - π SHA-256 verification - every download is checked against go.dev's official checksum
- π Shell integration - automatic
PATHandGOROOTinjection for PowerShell, Bash, Zsh, and Fish - β‘
gvsn exec- run a command with any Go version without changing the global default - π©Ί
gvsn doctor- diagnose your setup with actionable hints - π
gvsn outdated- check installed versions against go.dev for newer patch releases - π§Ή
gvsn prune- remove installed versions no longer referenced by any project - π
gvsn upgrade- self-update to the latest release from GitHub - π£
gvsn implode- completely remove gvsn and all installed versions cleanly - π Shell completions - Bash, Zsh, Fish, and PowerShell
- π₯οΈ Cross-platform - Windows, Linux, macOS Γ x86_64 and ARM64
irm https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/install.ps1 | iexInstalls
gvsn.exeto~\.local\bin, then automatically runsgvsn setupwhich adds the binary directory and~\.gvsn\current\binto your userPATHvia the Windows registry, and injects the shell hook into your PowerShell profile. The archive's checksum is verified againstchecksums.txtbefore extraction. The binary statically links the Windows C runtime, so it never depends on the Visual C++ Redistributable being installed.
curl -fsSL https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/install.sh | shInstalls
gvsnto~/.local/bin, then automatically runsgvsn setupwhich injects thegvsn envhook into your shell profile (~/.bashrc,~/.zshrc, etc.) and adds a static~/.gvsn/current/binPATH entry to your login profile (~/.profileor~/.zprofile) so that GUI applications like VSCode and GoLand can find Go without needing an interactive shell. The archive's checksum is verified againstchecksums.txtbefore extraction.
# πͺ Windows
$env:GVSN_INSTALL_DIR = "C:\tools\gvsn"; irm .../install.ps1 | iex# π§ Linux / π macOS
GVSN_INSTALL_DIR=~/.bin curl -fsSL .../install.sh | shgvsn doctorTwo equivalent ways to remove gvsn completely - all installed Go versions, the ~/.gvsn data directory, the binary, and every gvsn-managed line in your shell profile(s) / Windows PATH registry:
Option 1 - gvsn implode (if the binary still works):
gvsn implodeOption 2 - standalone uninstaller script (works even if the binary is broken or already gone, and cleans every supported shell's profile, not just the one currently detected):
# πͺ Windows
irm https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/uninstall.ps1 | iex# π§ Linux / π macOS
curl -fsSL https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/uninstall.sh | shBoth scripts show exactly what will be removed and ask for confirmation first. Add --dry-run to preview with nothing deleted, or --force to skip the prompt (a piped POSIX shell script can't prompt interactively, so use GVSN_UNINSTALL_FORCE=1 / GVSN_UNINSTALL_DRY_RUN=1 before the curl one-liner, or curl ... | sh -s -- --force to pass the flag through). They also sweep leftover temp files from an install/upgrade that was interrupted mid-way.
π‘ Anything you customised at install time (
GVSN_DIR,GVSN_INSTALL_DIR) should be set the same way before uninstalling, so the script cleans the right locations.
# π₯ Install the latest stable Go release
gvsn install latest
# π Activate it globally
gvsn use latest
# π Check the active version
gvsn current
# π Pin a version for the current project
gvsn local 1.22
# β‘ Run tests with a different version, without changing the global default
gvsn exec 1.21 go test ./...Downloads and installs a Go release from go.dev. The archive is verified against the official SHA-256 checksum before extraction.
gvsn install latest # π latest stable release
gvsn install 1.22 # π’ latest patch of Go 1.22
gvsn install 1.22.4 # π― exact version
gvsn install 1.22.4 --force # π reinstall even if already presentDownload tuning:
gvsn install latest --retries 5 # retry up to 5 times on error
gvsn install latest --retries 0 # fail immediately on first error| Flag | Default | Description |
|---|---|---|
--retries <N> |
3 |
Max retry attempts on network failure. Uses exponential back-off (1 s, 2 s, 4 s, β¦). |
π‘ If a download is interrupted (network drop, Ctrl-C), re-running the same
gvsn installcommand resumes from the last byte written - no data is re-downloaded.
Compiles a Go release directly from the official source tarball (go<X>.<Y>.<Z>.src.tar.gz). The resulting toolchain is installed into ~/.gvsn/versions/ alongside any binaries installed with gvsn install. Uses src/make.bash on Linux/macOS and src/make.bat on Windows.
gvsn build 1.24.0 # build an exact release
gvsn build 1.24 # build the latest patch of Go 1.24
gvsn build latest # build the latest stable release
gvsn build 1.24.0 --force # rebuild even if already installedStream every compiler line in real time (recommended for long builds):
gvsn build 1.24.0 -vWithout -v, gvsn shows a spinner with the current build phase and prints the last 100 lines automatically if the build fails.
With -v, every line from make.bash/make.bat is printed as it is produced:
β Έ Building packages and commands... 0:02:34
β go tool compile -std -trimpath ...
β go tool compile -std -trimpath ...
Disable CGO (faster build, no C toolchain needed):
gvsn build 1.24.0 --no-cgoSet a custom bootstrap compiler (must be already installed via gvsn):
gvsn build 1.24.0 --bootstrap 1.22.6Pass extra environment variables to make.bash:
gvsn build 1.24.0 --env GOAMD64=v3
gvsn build 1.24.0 --env GOAMD64=v3 --env CC=clangDownload tuning (source tarball and bootstrap download):
gvsn build 1.24.0 --retries 5 # retry up to 5 times on errorGo has been self-hosted since version 1.5 - compiling it from source requires a working Go installation as a bootstrap compiler. gvsn build resolves one automatically:
--bootstrap <version>- use a specific installed version (must be present viagvsn install)- Highest installed gvsn version - reused with no extra download
- Auto-download - if no Go version is installed at all, gvsn downloads the latest patch of the previous minor as a temporary bootstrap and removes it after the build
Building Go from source takes 5-15 minutes and requires approximately 3 GB of free disk space for the source tree, build artifacts, and final installation.
gvsn offers four ways to switch versions, each with a different scope:
| Command | Scope | Persisted where |
|---|---|---|
gvsn use / gvsn default |
Every shell, system-wide | ~/.gvsn/version |
gvsn local |
This project (and anyone who clones it) | .go-version file in the project |
gvsn shell |
This terminal session only | Not persisted - lives only in the shell's environment |
Resolution order when a new shell starts (or the shell hook re-checks): a gvsn shell session override wins if set, otherwise .go-version (walking up from the current directory) wins if found, otherwise the global default from gvsn use applies.
Sets the global default Go version. The version must already be installed.
gvsn use latest
gvsn use 1.22
gvsn use 1.22.4π‘ The change takes effect in any new terminal session, or immediately after reloading your profile.
Writes a .go-version file in the current directory. gvsn reads this file on every shell startup and activates the pinned version automatically.
# In your project root:
gvsn local 1.21.9The file contains a plain version string (
go1.21.9) and can be committed to version control so every contributor uses the same toolchain.
β οΈ If the pinned version is not installed, gvsn prints a warning and falls back to the global default.
π
gvsn local latestwrites the literal stringlatestto.go-versioninstead of resolving it to a specific version. This means the pin tracks the current stable release over time rather than freezing on whatever was newest when you ran the command - different fromgvsn local 1.22orgvsn local 1.22.4, which always pin an exact, unchanging version.
Activates a Go version for the current terminal session only - no files are written, nothing is persisted. Closing the terminal (or running --unset) reverts to whatever .go-version or the global default says.
gvsn shell 1.21 # π― activate 1.21 for this session only
gvsn shell --unset # β©οΈ revert to the file-based versionRequires the shell wrapper injected by
gvsn setupto take effect immediately in the current shell. Without it, runeval "$(gvsn shell 1.21)"manually.
Removes an installed Go version from disk. Refuses to remove the currently active version. Asks for confirmation unless --force is passed.
gvsn uninstall 1.21.9
gvsn uninstall 1.21.9 --force # π skip the confirmation promptRemoves installed Go versions that are no longer referenced by anything gvsn knows about, freeing disk space without you having to track down stale toolchains by hand.
A version counts as referenced when it matches the global default, or appears in a .go-version file found by walking up from the current directory (or found while scanning --scan-dir, up to 5 levels deep). Everything else is offered for removal.
gvsn prune # π shows what would be removed, asks for confirmation
gvsn prune --dry-run # π preview only, nothing is deleted
gvsn prune --force # π skip the confirmation prompt
gvsn prune --scan-dir ~/projects # π also check .go-version files under this directoryLists all locally installed Go versions. The active version is highlighted.
go1.23.0 (active)
go1.22.4
go1.21.9
Lists stable Go versions available for download from go.dev.
gvsn list-remote # π latest patch per minor (compact view)
gvsn list-remote --all # π every patch releaseAlready-installed versions are marked with β.
Checks every installed Go version against go.dev and reports whether a newer patch release exists for the same major.minor line.
gvsn outdated Installed Latest patch Status
----------------------------------------------------
go1.22.4 go1.22.4 β up to date
go1.21.6 go1.21.9 β 3 patches behind
Prints the active Go version and where it came from.
go1.22.4 (local .go-version)
or
go1.23.0 (global)
Prints the bin/ directory of the active (or specified) version. Useful for scripting.
gvsn path # active version
gvsn path 1.21 # specific version
export GOROOT=$(dirname $(gvsn path))Emits shell commands that set PATH and GOROOT for the active version. This is what the shell hook calls on every prompt.
eval "$(gvsn env)" # π auto-detect shell
gvsn env --shell bash
gvsn env --shell zsh
gvsn env --shell fish# πͺ PowerShell
gvsn env --shell powershell | Out-String | Invoke-ExpressionPerforms all environment configuration for gvsn. The install scripts run this automatically; you only need it manually after moving the binary, changing your shell, or troubleshooting.
gvsn setup # auto-detect shell
gvsn setup --shell zsh # configure a specific shell explicitly
gvsn setup --reset # strip all previous gvsn config and re-apply cleanly
gvsn setup --shell bash --resetWhat gvsn setup configures:
| Platform | What it does |
|---|---|
| Linux / macOS | Injects # gvsn init + # gvsn wrapper into the interactive profile (~/.bashrc, ~/.zshrc, etc.). Also injects a static export PATH line into the login profile (~/.profile for bash, ~/.zprofile for zsh) so ~/.gvsn/current/bin is visible to GUI apps (VSCode, GoLand, display managers) that don't source the interactive profile. |
| Windows | Injects # gvsn init + # gvsn wrapper into the PowerShell profile. Adds the gvsn binary directory and ~\.gvsn\current\bin to the user PATH in the Windows registry (HKCU\Environment) so all apps - including GUI editors - see Go without requiring a shell session. |
Shell validation: if --shell <name> is passed, gvsn checks that the shell is actually installed before writing anything. If not found, it exits with an error listing which shells are available on the system.
--reset flag: strips every # gvsn ... block from all managed profiles (and the Windows registry) and re-applies configuration from scratch. Only gvsn-managed content is touched - all other profile content is preserved.
Re-running
gvsn setupwithout--resetis always safe - existing up-to-date blocks are left unchanged and stale ones are updated automatically.
Runs any command with a specific Go version injected into PATH and GOROOT, without changing the global default.
# ποΈ Build with Go 1.21 while Go 1.22 is the global default
gvsn exec 1.21 go build ./...
# π§ͺ Run tests on multiple versions in CI
gvsn exec 1.20 go test ./...
gvsn exec 1.21 go test ./...
gvsn exec 1.22 go test ./...
# π Check the exact Go binary
gvsn exec 1.22.4 go versionThe exit code of the subprocess is forwarded to the calling process.
Checks your gvsn installation and reports issues with actionable hints:
- π
gvsnbinary is inPATH - π A global Go version is set
- πΎ The global version is installed on disk
- π
GOROOTresolves to a valid directory - π The
gvsn envhook is present in the shell profile - π The local
.go-version(if any) is installed
gvsn doctor
gvsn doctor --shell zshExits with code
1if any issue is found - perfect for CI health checks.
Self-updates gvsn to the latest release published on GitHub.
gvsn upgrade # π check and update if a newer version exists
gvsn upgrade --force # π reinstall the latest even if already up to date
gvsn upgrade --retries 5 # retry up to 5 times on errorπ On Unix the replacement is atomic (same-filesystem rename). On Windows the old binary is renamed first to free its name, then the new binary takes the original path. A rollback is attempted automatically if the replacement fails.
π gvsn also checks for updates passively in the background after most commands (cached for 24h, never adds noticeable delay) and prints a short notice when a newer version is available. It never runs for
env/path/shell/completions(called on every shell prompt) orupgradeitself. Disable it entirely withGVSN_NO_UPDATE_CHECK=1; it's also automatically skipped whenCIis set.
Completely removes gvsn and everything it manages from the system.
gvsn implode # ποΈ shows a summary, asks for confirmation
gvsn implode --force # π₯ removes everything immediately, no questions askedWhat gets removed:
- π The entire
~/.gvsn/data directory (all installed Go versions) - π§ The
gvsnbinary itself - π Every gvsn-managed line from your interactive shell profile (
~/.bashrc,~/.zshrc, PowerShell profile, etc.) - π The static PATH entry from your login profile (
~/.profile,~/.zprofile) on Linux/macOS - ποΈ The gvsn entries from the Windows user PATH registry key (
HKCU\Environment) on Windows
β οΈ This operation is irreversible. Your installed Go versions will be deleted. Usegvsn upgradeinstead if you just want to update.
π©Ή If the
gvsnbinary is broken or already gone, use the standalone uninstaller script instead (see Uninstallation near the top of this README) - it does not depend ongvsnitself.
Prints a shell completion script to stdout.
# π§ Bash
gvsn completions bash > ~/.local/share/bash-completion/completions/gvsn
# π Zsh
gvsn completions zsh > "${fpath[1]}/_gvsn"
# π Fish
gvsn completions fish > ~/.config/fish/completions/gvsn.fish
# πͺ PowerShell
gvsn completions powershell >> $PROFILEAll commands that accept a version support these forms:
| Input | Meaning |
|---|---|
latest |
π Newest stable release |
1.22 |
π’ Latest installed patch of Go 1.22 |
1.22.4 |
π― Exact version go1.22.4 |
go1.22.4 |
β
Same as 1.22.4 (prefix accepted) |
Place a .go-version file in any directory:
go1.22.4
gvsn walks up the directory tree from the current working directory (up to 20 levels) looking for .go-version. When found, it takes precedence over the global default.
π The file is compatible with other tools such as goenv and the VS Code Go extension.
After running gvsn setup, two things are configured in your shell:
1. Interactive profile - the gvsn env hook, injected once by gvsn setup:
| Shell | Profile file | Hook |
|---|---|---|
| π§ Bash | ~/.bashrc |
eval "$(gvsn env --shell bash)" |
| π Zsh | ~/.zshrc |
eval "$(gvsn env --shell zsh)" |
| π Fish | ~/.config/fish/config.fish |
gvsn env --shell fish | source |
| πͺ PowerShell | ~/Documents/PowerShell/profile.ps1 |
gvsn env --shell powershell | Out-String | Invoke-Expression |
On every new interactive shell session the hook:
- π Reads the active version (
.go-versionβ global default) - β Prepends the version's
bin/directory toPATH - π Sets
GOROOTto the version's root directory
2. Login profile / registry - a static PATH entry so GUI apps find Go:
| Platform | Where | What |
|---|---|---|
| π§ Linux (bash) | ~/.profile |
export PATH="$HOME/.gvsn/current/bin:$PATH" |
| π Linux (zsh) | ~/.zprofile |
export PATH="$HOME/.gvsn/current/bin:$PATH" |
| πͺ Windows | HKCU\Environment |
gvsn dir + ~\.gvsn\current\bin added to user PATH |
This login profile entry is what makes go visible to VSCode, GoLand, and other GUI editors that launch outside of an interactive shell session.
π No daemons, no background processes, no side effects.
| Variable | Default | Description |
|---|---|---|
GVSN_DIR |
~/.gvsn |
π Root directory for all gvsn data |
GVSN_NO_UPDATE_CHECK |
unset | π Set to disable the background update notice |
~/.gvsn/
|-- version # active global version (plain text)
|-- current -> versions/go1.23.0/ # symlink/junction updated by gvsn use
|-- versions/
| |-- go1.22.4/ # extracted Go toolchain
| | |-- bin/
| | |-- src/
| | `-- ...
| `-- go1.23.0/
`-- tmp/ # download staging area (cleaned after install)
The current symlink (junction on Windows) always points to the active version. The login profile PATH entry points to ~/.gvsn/current/bin, which means GUI applications always see whichever version was last activated with gvsn use - no shell restart required.
Requires Rust 1.75 or newer. No system dependencies - TLS is handled by rustls (pure Rust, no OpenSSL needed).
git clone https://github.com/jhonsferg/gvsn.git
cd gvsn
cargo build --releaseThe binary is placed at target/release/gvsn (or gvsn.exe on Windows).
# β
Run the self-check after building
./target/release/gvsn doctorReleases are automated via GitHub Actions. Pushing a version tag triggers cross-compilation for all supported targets:
| Artifact | Target | Notes |
|---|---|---|
gvsn_windows_x86_64.zip |
x86_64-pc-windows-msvc |
β‘ static binary (CRT) |
gvsn_windows_arm64.zip |
aarch64-pc-windows-msvc |
β‘ static binary (CRT) |
gvsn_linux_x86_64.tar.gz |
x86_64-unknown-linux-musl |
β‘ static binary |
gvsn_linux_aarch64.tar.gz |
aarch64-unknown-linux-musl |
β‘ static binary |
gvsn_linux_armv7.tar.gz |
armv7-unknown-linux-musleabihf |
β‘ static binary |
gvsn_linux_386.tar.gz |
i686-unknown-linux-musl |
β‘ static binary |
gvsn_linux_riscv64.tar.gz |
riscv64gc-unknown-linux-gnu |
links system glibc (no musl target) |
gvsn_linux_s390x.tar.gz |
s390x-unknown-linux-gnu |
IBM Z - links system glibc |
gvsn_linux_ppc64le.tar.gz |
powerpc64le-unknown-linux-gnu |
IBM POWER LE - links system glibc |
gvsn_android_aarch64.tar.gz |
aarch64-linux-android |
π€ Termux, links bionic |
gvsn_darwin_x86_64.tar.gz |
x86_64-apple-darwin |
links libSystem (normal on macOS) |
gvsn_darwin_aarch64.tar.gz |
aarch64-apple-darwin |
π Apple Silicon, links libSystem |
Each release also includes checksums.txt with SHA-256 hashes for all artifacts, plus SBOM files in CycloneDX and SPDX formats.
Releases are created automatically: every merge to main that passes CI triggers the auto-tag-and-release job, which bumps the version based on conventional commit prefixes (feat -> minor, fix -> patch) and dispatches the release build.
Only trust downloads that come from GitHub Releases on this exact repository:
https://github.com/jhonsferg/gvsn/releases
Do not trust:
- Download buttons or "release" links that point to a raw file inside a repo tree (e.g.
.../raw/refs/heads/main/...) instead of the Releases page. - Copies of this project hosted under a different GitHub account, even if the name, README, or commit history look identical - clones are not maintained by us and are not covered by this project's security process.
- Any instructions telling you to bypass a Windows SmartScreen, macOS Gatekeeper, or antivirus warning to run a
gvsnbinary. Legitimate releases are checksummed (checksums.txt) and do not require disabling OS protections.
This project's real binaries are always built by the release.yml GitHub Action in this repository and shipped with SHA-256 checksums and SBOM files - never as a standalone .zip/.exe committed directly into source control.
If you find a copy of this project distributing something other than the source in this repository (especially compiled binaries not produced by our CI), please report it - see SECURITY.md.
π‘οΈ If your antivirus or Windows Defender flags a genuine release from this repository, that's a false positive, not a compromised download - see SECURITY.md for why this happens and what to do.
MIT - see LICENSE.
Made with π¦ Rust Β· Maintained with β€οΈ