gvsn - Go Version Manager
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
gvsnfrom 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.
β¨ What makes gvsn different?
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.
π Features
- π₯ 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
π¦ Installation
πͺ Windows (PowerShell)
irm https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/install.ps1 | iex
Installs
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.
π§ Linux and π macOS
|
Installs
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.
π Custom install directory
# πͺ Windows
$env:GVSN_INSTALL_DIR = "C:\tools\gvsn"; irm .../install.ps1 | iex
# π§ Linux / π macOS
GVSN_INSTALL_DIR=/.bin |
β Verify the installation
ποΈ Uninstallation
Two 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):
Option 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
|
Both 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.
β‘ Quick Start
# π₯ Install the latest stable Go release
# π Activate it globally
# π Check the active version
# π Pin a version for the current project
# β‘ Run tests with a different version, without changing the global default
π Commands
π₯ gvsn install <version>
Downloads and installs a Go release from go.dev. The archive is verified against the official SHA-256 checksum before extraction.
Download tuning:
| 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.
π¨ gvsn build <version>
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.
Stream every compiler line in real time (recommended for long builds):
Without -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):
Set a custom bootstrap compiler (must be already installed via gvsn):
Pass extra environment variables to make.bash:
Download tuning (source tarball and bootstrap download):
Bootstrap compiler
Go 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
Time and disk requirements
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.
π― Choosing how to activate a version
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.
π gvsn use <version> Β· gvsn default <version>
Sets the global default Go version. The version must already be installed.
π‘ The change takes effect in any new terminal session, or immediately after reloading your profile.
π gvsn local <version>
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:
The 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.
π gvsn shell [version] [--unset]
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.
Requires the shell wrapper injected by
gvsn setupto take effect immediately in the current shell. Without it, runeval "$(gvsn shell 1.21)"manually.
ποΈ gvsn uninstall <version> [--force]
Removes an installed Go version from disk. Refuses to remove the currently active version. Asks for confirmation unless --force is passed.
π§Ή gvsn prune [--force] [--dry-run] [--scan-dir <path>]
Removes 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 list
Lists all locally installed Go versions. The active version is highlighted.
go1.23.0 (active)
go1.22.4
go1.21.9
π gvsn list-remote
Lists stable Go versions available for download from go.dev.
Already-installed versions are marked with β.
π gvsn outdated
Checks every installed Go version against go.dev and reports whether a newer patch release exists for the same major.minor line.
Installed Latest patch Status
----------------------------------------------------
go1.22.4 go1.22.4 β up to date
go1.21.6 go1.21.9 β 3 patches behind
π gvsn current
Prints the active Go version and where it came from.
go1.22.4 (local .go-version)
or
go1.23.0 (global)
π gvsn path [version]
Prints the bin/ directory of the active (or specified) version. Useful for scripting.
π gvsn env [--shell <name>]
Emits shell commands that set PATH and GOROOT for the active version. This is what the shell hook calls on every prompt.
# πͺ PowerShell
gvsn env --shell powershell | Out-String | Invoke-Expression
π§ gvsn setup [--shell <name>] [--reset]
Performs 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.
What 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.
β‘ gvsn exec <version> <command> [argsβ¦]
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
# π§ͺ Run tests on multiple versions in CI
# π Check the exact Go binary
The exit code of the subprocess is forwarded to the calling process.
π©Ί gvsn doctor [--shell <name>]
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
Exits with code
1if any issue is found - perfect for CI health checks.
π gvsn upgrade [--force]
Self-updates gvsn to the latest release published on GitHub.
π 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.
π£ gvsn implode [--force]
Completely removes gvsn and everything it manages from the system.
What 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. Use
gvsn 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.
π gvsn completions <shell>
Prints a shell completion script to stdout.
# π§ Bash
# π Zsh
# π Fish
# πͺ PowerShell
π’ Version Syntax
All 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) |
π Per-project Versions
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.
π Shell Integration
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.
βοΈ Configuration
| Variable | Default | Description |
|---|---|---|
GVSN_DIR |
~/.gvsn |
π Root directory for all gvsn data |
GVSN_NO_UPDATE_CHECK |
unset | π Set to disable the background update notice |
π Directory layout
~/.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.
π οΈ Building from Source
Requires Rust 1.75 or newer. No system dependencies - TLS is handled by rustls (pure Rust, no OpenSSL needed).
The binary is placed at target/release/gvsn (or gvsn.exe on Windows).
# β
Run the self-check after building
π¦ Release Artifacts
Releases 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.
π Verifying you're on the real project
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.
π License
MIT - see LICENSE.
Made with π¦ Rust Β· Maintained with β€οΈ