gvsn 1.0.1

A fast, cross-platform Go version manager written in Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
<div align="center">

<img src="assets/images/ferris-and-gopher.png" alt="Ferris and Gopher" width="180" />

# 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.

[![Release](https://img.shields.io/github/v/release/jhonsferg/gvsn?style=for-the-badge&logo=github&label=Release&color=blueviolet)](https://github.com/jhonsferg/gvsn/releases/latest)
[![CI](https://img.shields.io/github/actions/workflow/status/jhonsferg/gvsn/ci.yml?branch=main&style=for-the-badge&logo=githubactions&logoColor=white&label=CI)](https://github.com/jhonsferg/gvsn/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge&logo=opensourceinitiative&logoColor=white)](LICENSE)

[![Rust](https://img.shields.io/badge/Built_with-Rust-orange?style=for-the-badge&logo=rust&logoColor=white)](https://www.rust-lang.org/)
[![TLS](https://img.shields.io/badge/TLS-rustls_%28no_OpenSSL%29-lightgrey?style=for-the-badge&logo=letsencrypt&logoColor=white)](https://github.com/rustls/rustls)

[![Windows](https://img.shields.io/badge/Windows-0078D4?style=for-the-badge&logo=windows11&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)](https://github.com/jhonsferg/gvsn/releases/latest)
[![macOS](https://img.shields.io/badge/macOS-000000?style=for-the-badge&logo=apple&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![Android](https://img.shields.io/badge/Android_%28Termux%29-3DDC84?style=for-the-badge&logo=android&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)

[![x86_64](https://img.shields.io/badge/x86__64-555555?style=for-the-badge&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![ARM64](https://img.shields.io/badge/ARM64-0091BD?style=for-the-badge&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![ARMv7](https://img.shields.io/badge/ARMv7-0091BD?style=for-the-badge&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![386](https://img.shields.io/badge/386-555555?style=for-the-badge&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![riscv64](https://img.shields.io/badge/RISC--V_64-2C3E50?style=for-the-badge&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![s390x](https://img.shields.io/badge/s390x-1F3A8A?style=for-the-badge&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)
[![ppc64le](https://img.shields.io/badge/ppc64le-1F3A8A?style=for-the-badge&logoColor=white)](https://github.com/jhonsferg/gvsn/releases/latest)

</div>

---

> [!WARNING]
> **Only get `gvsn` from this repository's official [Releases page](https://github.com/jhonsferg/gvsn/releases) or the install commands below.** Clones of this repository exist that distribute unofficial binaries under the same name. See [SECURITY.md](SECURITY.md#malicious-forks--clones) for known cases, the [plain-language walkthrough](docs/security/anatomy-of-a-malicious-clone.md) of how one clone's malware works, or the [full technical writeup](docs/security/incidents/2026-06-malicious-clone-foremost-headsail607.md) 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 `~/.gvsn` in 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 -v` streams 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 setup` configures 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 upgrade` downloads and replaces the binary in-place.
- **Clean uninstall** - `gvsn implode` removes everything gvsn ever touched.

### πŸ¦€ Why Rust, not Go?

A fair question for a Go tool. A few honest reasons, not a verdict on Go:

- **Memory safety for the riskiest operations.** `gvsn upgrade` replaces its own running binary,
  `gvsn setup` writes to your shell profile and the Windows registry PATH, and `gvsn implode`
  deletes 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 `cross` and 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.

---

## πŸš€ 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-version` file; 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 `PATH` and `GOROOT` injection 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)

```powershell
irm https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/install.ps1 | iex
```

> Installs `gvsn.exe` to `~\.local\bin`, then automatically runs `gvsn setup` which adds the binary directory and `~\.gvsn\current\bin` to your user `PATH` via the Windows registry, and injects the shell hook into your PowerShell profile. The archive's checksum is verified against `checksums.txt` before extraction. The binary statically links the Windows C runtime, so it never depends on the Visual C++ Redistributable being installed.

### 🐧 Linux and 🍎 macOS

```sh
curl -fsSL https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/install.sh | sh
```

> Installs `gvsn` to `~/.local/bin`, then automatically runs `gvsn setup` which injects the `gvsn env` hook into your shell profile (`~/.bashrc`, `~/.zshrc`, etc.) and adds a static `~/.gvsn/current/bin` PATH entry to your login profile (`~/.profile` or `~/.zprofile`) so that GUI applications like VSCode and GoLand can find Go without needing an interactive shell. The archive's checksum is verified against `checksums.txt` before extraction.

### πŸ“‚ Custom install directory

```powershell
# πŸͺŸ Windows
$env:GVSN_INSTALL_DIR = "C:\tools\gvsn"; irm .../install.ps1 | iex
```

```sh
# 🐧 Linux / 🍎 macOS
GVSN_INSTALL_DIR=~/.bin curl -fsSL .../install.sh | sh
```

### βœ… Verify the installation

```sh
gvsn doctor
```

---

## πŸ—‘οΈ 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):

```sh
gvsn implode
```

**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):

```powershell
# πŸͺŸ Windows
irm https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/uninstall.ps1 | iex
```

```sh
# 🐧 Linux / 🍎 macOS
curl -fsSL https://raw.githubusercontent.com/jhonsferg/gvsn/main/install/uninstall.sh | sh
```

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

```sh
# πŸ“₯ 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 ./...
```

---

## πŸ“– 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.

```sh
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 present
```

**Download tuning:**

```sh
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 install` command 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.

```sh
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 installed
```

**Stream every compiler line in real time** (recommended for long builds):

```sh
gvsn build 1.24.0 -v
```

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):

```sh
gvsn build 1.24.0 --no-cgo
```

**Set a custom bootstrap compiler** (must be already installed via gvsn):

```sh
gvsn build 1.24.0 --bootstrap 1.22.6
```

**Pass extra environment variables** to `make.bash`:

```sh
gvsn build 1.24.0 --env GOAMD64=v3
gvsn build 1.24.0 --env GOAMD64=v3 --env CC=clang
```

**Download tuning** (source tarball and bootstrap download):

```sh
gvsn build 1.24.0 --retries 5    # retry up to 5 times on error
```

#### 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:

1. **`--bootstrap <version>`** - use a specific installed version (must be present via `gvsn install`)
2. **Highest installed gvsn version** - reused with no extra download
3. **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.

```sh
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.

---

### πŸ“Œ `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.

```sh
# In your project root:
gvsn local 1.21.9
```

> 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 latest` writes the literal string `latest` to `.go-version` instead 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 from `gvsn local 1.22` or `gvsn 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.

```sh
gvsn shell 1.21        # 🎯 activate 1.21 for this session only
gvsn shell --unset     # ↩️  revert to the file-based version
```

> Requires the shell wrapper injected by `gvsn setup` to take effect immediately in the current shell. Without it, run `eval "$(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.

```sh
gvsn uninstall 1.21.9
gvsn uninstall 1.21.9 --force   # πŸ”‡ skip the confirmation prompt
```

---

### 🧹 `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.

```sh
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 directory
```

---

### πŸ“‹ `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.

```sh
gvsn list-remote          # πŸ“„ latest patch per minor (compact view)
gvsn list-remote --all    # πŸ“œ every patch release
```

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.

```sh
gvsn outdated
```

```
  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.

```sh
gvsn path              # active version
gvsn path 1.21         # specific version
export GOROOT=$(dirname $(gvsn path))
```

---

### 🐚 `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.

```sh
eval "$(gvsn env)"               # πŸ” auto-detect shell
gvsn env --shell bash
gvsn env --shell zsh
gvsn env --shell fish
```

```powershell
# πŸͺŸ 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.

```sh
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 --reset
```

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 setup` without `--reset` is 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**.

```sh
# πŸ—οΈ 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 version
```

> 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:

- πŸ” `gvsn` binary is in `PATH`
- 🌍 A global Go version is set
- πŸ’Ύ The global version is installed on disk
- πŸ“‚ `GOROOT` resolves to a valid directory
- 🐚 The `gvsn env` hook is present in the shell profile
- πŸ“Œ The local `.go-version` (if any) is installed

```sh
gvsn doctor
gvsn doctor --shell zsh
```

> Exits with code `1` if any issue is found - perfect for CI health checks.

---

### πŸ”„ `gvsn upgrade [--force]`

Self-updates gvsn to the latest release published on GitHub.

```sh
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) or `upgrade` itself. Disable it entirely with `GVSN_NO_UPDATE_CHECK=1`; it's also automatically skipped when `CI` is set.

---

### πŸ’£ `gvsn implode [--force]`

**Completely removes gvsn** and everything it manages from the system.

```sh
gvsn implode           # πŸ—‘οΈ shows a summary, asks for confirmation
gvsn implode --force   # πŸ’₯ removes everything immediately, no questions asked
```

What gets removed:

- πŸ“ The entire `~/.gvsn/` data directory (all installed Go versions)
- πŸ”§ The `gvsn` binary 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 upgrade` instead if you just want to update.

> 🩹 If the `gvsn` binary is broken or already gone, use the standalone uninstaller script instead (see **Uninstallation** near the top of this README) - it does not depend on `gvsn` itself.

---

### 🏁 `gvsn completions <shell>`

Prints a shell completion script to stdout.

```sh
# 🐧 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 >> $PROFILE
```

---

## πŸ”’ 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](https://github.com/syndbg/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:

1. πŸ” Reads the active version (`.go-version` β†’ global default)
2. βž• Prepends the version's `bin/` directory to `PATH`
3. πŸ“‚ Sets `GOROOT` to 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](https://rustup.rs) 1.75 or newer. No system dependencies - TLS is handled by [rustls](https://github.com/rustls/rustls) (pure Rust, no OpenSSL needed).

```sh
git clone https://github.com/jhonsferg/gvsn.git
cd gvsn
cargo build --release
```

The binary is placed at `target/release/gvsn` (or `gvsn.exe` on Windows).

```sh
# βœ… Run the self-check after building
./target/release/gvsn doctor
```

---

## πŸ“¦ 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 `gvsn` binary. 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](.github/workflows/release.yml) 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](SECURITY.md#malicious-forks--clones).

> πŸ›‘οΈ 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](SECURITY.md#antivirus--windows-defender-false-positives) for why this happens and what to do.

---

## πŸ“„ License

MIT - see [LICENSE](LICENSE).

---

<div align="center">

Made with πŸ¦€ Rust Β· Maintained with ❀️

</div>