gup
Update binaries installed by "go install"
About gup
This large README is shown as a plain-text preview. Read the full README at the source
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
[](https://github.com/avelino/awesome-go)
[](https://github.com/nao1215/gup/actions/workflows/reviewdog.yml)

[](https://github.com/nao1215/atago)
[](https://github.com/nao1215/himorime)
[](https://pkg.go.dev/github.com/nao1215/gup)

[](https://github.com/nao1215/gup/releases)
[](https://scorecard.dev/viewer/?uri=github.com/nao1215/gup)

gup updates and manages the global Go command-line tools in your `$GOBIN`. `go install` places each program in `$GOBIN` (`$GOPATH/bin`) but never updates it again, keeps no manifest of what it installed, and offers no way to hold a tool at a version you depend on. gup manages that tool set: it brings the whole set up to date in parallel, can `pin` selected tools to exact versions, and adds the management commands `go install` lacks: `list`/`check` what is installed, `remove` binaries, `export`/`import` the set to reproduce it on another machine, and `migrate` it to a new `$GOBIN`. Runs on Windows, macOS, and Linux.
Documentation: **https://nao1215.github.io/gup/**
## Supported OS (unit testing with GitHub Actions)
- Linux
- Mac (macOS 13 Ventura or newer, the range Go 1.27 supports)
- Windows
### Supported Go versions
Unit tests run on Go 1.26 and 1.27. Building from source needs Go 1.26 or newer. The release binaries are built with the latest Go 1.27 patch release, so on macOS they need macOS 13 Ventura or newer.
## How to install
gup is packaged in homebrew-core, the winget community repository, the mise and aqua registries, nixpkgs, and the AUR, in addition to `go install` and the prebuilt packages on the release page.
### Use "go install"
If you do not have the Go development environment installed on your system, please install it from the [official website](https://go.dev/doc/install).
```
go install github.com/nao1215/gup@latest
```
Building from source needs Go 1.26 or newer. On an older Go, install a prebuilt release binary or a package (see below) instead.
### Use homebrew
gup is in homebrew-core, so no tap is required:
```shell
brew install gup
```
The GoReleaser-built formula in `nao1215/tap` is still published as an alternative. It installs the prebuilt release binary instead of building from source:
```shell
brew install nao1215/tap/gup
```
### Use winget (Windows)
```shell
winget install --id nao1215.gup
```
### Use mise-en-place
```shell
mise use -g gup@latest
```
### Use nix (Nix profile)
```shell
nix profile install nixpkgs#gogup
```
### Use aqua
gup is registered in the [aqua](https://aquaproj.github.io/) standard registry. Add it to your `aqua.yaml`:
```shell
aqua g -i nao1215/gup
```
### Use the AUR (Arch Linux)
Two community-maintained packages are available: [`gup`](https://aur.archlinux.org/packages/gup) builds from source, [`gup-bin`](https://aur.archlinux.org/packages/gup-bin) installs the release binary.
```shell
paru -S gup # or: yay -S gup
paru -S gup-bin # prebuilt binary
```
### Install from Package or Binary
[The release page](https://github.com/nao1215/gup/releases) contains packages in .deb, .rpm, and .apk formats for `amd64` and `arm64`, plus `.tar.gz` archives for Linux/macOS and `.zip` archives for Windows. gup command uses the go command internally, so the golang installation is required.
Download the package that matches your distribution and architecture, then:
```shell
# Debian, Ubuntu
$ sudo dpkg -i gup_1.8.1_linux_amd64.deb
# Fedora, RHEL, openSUSE
$ sudo rpm -Uvh gup_1.8.1_linux_amd64.rpm
# Alpine Linux
$ sudo apk add --allow-untrusted gup_1.8.1_linux_amd64.apk
```
Replace `1.8.1` with the release you downloaded and `amd64` with `arm64` where applicable. The packages also install the bash, fish, and zsh completion files.
## Verifying release integrity
Every release ships supply-chain metadata so you can verify what you download:
- Signed checksums: `checksums.txt` is signed with [cosign](https://github.com/sigstore/cosign) (keyless), producing `checksums.txt.sigstore.json`.
- SBOM: an SPDX Software Bill of Materials is attached to each release archive.
- Build provenance: SLSA build provenance is attested via GitHub OIDC, and is also attached to the release as `multiple.intoto.jsonl`.
Verify the signed checksums (then check your archive against `checksums.txt`):
```shell
cosign verify-blob \
--bundle checksums.txt.sigstore.json \
--certificate-identity-regexp 'https://github.com/nao1215/gup/\.github/workflows/release\.yml@refs/tags/.*' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
checksums.txt
sha256sum --check --ignore-missing checksums.txt
```
Verify the build provenance of a downloaded artifact with the GitHub CLI:
```shell
gh attestation verify gup_<version>_<os>_<arch>.tar.gz --repo nao1215/gup
```
Or download the `multiple.intoto.jsonl` release asset and verify the archive against it with [slsa-verifier](https://github.com/slsa-framework/slsa-verifier), which checks the provenance signature against the Sigstore transparency log:
```shell
slsa-verifier verify-artifact gup_<version>_<os>_<arch>.tar.gz \
--provenance-path multiple.intoto.jsonl \
--source-uri github.com/nao1215/gup \
--source-tag v<version>
```
## How to use
### Check the gup version
Print the version with either the `version` subcommand or the top-level `--version`/`-V` flag.
```shell
$ gup --version
$ gup version
```
### Update all binaries
`gup update` updates every binary under `$GOBIN`, in parallel.

### Update the specified binary
If you want to update only the specified binaries, you specify multiple command names separated by space.
```shell
$ gup update subaru gup ubume
update binary under $GOPATH/bin or $GOBIN
[1/3] github.com/nao1215/gup (v0.7.0 to v0.7.1, go1.20.1 to go1.22.4)
[2/3] github.com/nao1215/subaru (Already up-to-date: v1.0.2 / go1.22.4)
[3/3] github.com/nao1215/ubume/cmd/ubume (Already up-to-date: v1.4.1 / go1.22.4)
```
### Exclude binaries during gup update
If you don't want to update some binaries simply specify binaries which should not be updated separated using ',' without spaces as a delimiter.
Also works in combination with --dry-run. A name that is not installed is ignored, so the same list can be shared between machines; if it looks like a typo of an installed binary, gup prints a "did you mean" warning.
```shell
$ gup update --exclude=gopls,golangci-lint //--exclude or -e, this example will exclude 'gopls' and 'golangci-lint'
```
### Update binaries with @main, @master, or @latest
If you want to control update source per binary, use the following options:
- `--main` (`-m`): update by `@main` (falls back to `@master` only when the repository has no `main` branch)
- `--master`: update by `@master`
- `--latest`: update by `@latest`
The `@main` → `@master` fallback applies only to a missing `main` branch. Build, network, authentication, timeout, and cancellation failures are reported as-is and never silently install `@master`.
The selected channel is saved to `gup.json` and reused by future `gup update` runs.
```shell
$ gup update --main=gup,lazygit --master=sqly --latest=air
```
### Pin a tool to a specific version
Use `pin` when a global tool must stay on a specific version, for example when it needs to match CI or a team-wide development environment.
```shell
$ gup pin golangci-lint v1.62.0
$ gup update
```
`gup update` keeps a pinned tool at that version while the rest of the tool set updates.
A pin must name one fixed version, so that `go install` can never resolve it to different code later. `gup pin` accepts a full version (`v1.62.0`), a prerelease (`v2.0.0-rc.1`), a `+incompatible` version of a v2+ module (`v2.0.0+incompatible`), or a pseudo-version to pin a commit (`v0.0.0-20240102150405-abcdef123456`). It rejects branch names, commit hashes, abbreviated versions such as `v1` or `v1.2`, and version queries such as `>=v1.2.0`, all of which the go command resolves when it runs. To pin a commit, use its pseudo-version: `go list -m <module>@<commit>` prints it. The check needs neither the network nor the go command, so a well-formed version that does not exist is only reported by `gup update`.
The pin is stored in `gup.json`:
```json
{
"schema_version": 2,
"packages": [
{
"name": "golangci-lint",
"import_path": "github.com/golangci/golangci-lint/cmd/golangci-lint",
"version": "v1.62.0",
"channel": "pinned"
}
]
}
```
To allow the tool to update again:
```shell
$ gup unpin golangci-lint
```
If a hand-edited `gup.json` holds a pin that is not one fixed version, every command that reads it (`update`, `import`, `check`, `pin`, ...) fails and names the file, the tool, and the value. gup never falls back to `@latest` or removes the pin on its own. Fix the entry by setting its `"version"` to a full version or a pseudo-version, or set its `"channel"` to `"latest"` to unpin it.
### List up command name with package path and version under $GOPATH/bin
list subcommand print command information under $GOPATH/bin or $GOBIN. The output information is the command name, package path, and command version.

### Remove the specified binary
If you want to remove a command under $GOPATH/bin or $GOBIN, use the remove subcommand. The remove subcommand asks if you want to remove it before removing it.
```shell
$ gup remove subaru gal ubume
gup:CHECK: remove /home/nao/.go/bin/subaru? [Y/n] Y
removed /home/nao/.go/bin/subaru
gup:CHECK: remove /home/nao/.go/bin/gal? [Y/n] n
cancel removal /home/nao/.go/bin/gal
gup:CHECK: remove /home/nao/.go/bin/ubume? [Y/n] Y
removed /home/nao/.go/bin/ubume
```
If you want to force the removal, use the --force option.
```shell
$ gup remove --force gal
removed /home/nao/.go/bin/gal
```
### Check if the binary is the latest version
If you want to know if the binary is the latest version, use the check subcommand. check subcommand checks if the binary is the latest version and displays the name of the binary that needs to be updated.
```shell
$ gup check
check binary under $GOPATH/bin or $GOBIN
[ 1/33] github.com/cheat/cheat (Already up-to-date: v0.0.0-20211009161301-12ffa4cb5c87 / go1.22.4)
[ 2/33] fyne.io/fyne/v2 (current: v2.1.3, latest: v2.1.4 / current: go1.20.2, installed: go1.22.4)
:
[33/33] github.com/nao1215/ubume (Already up-to-date: v1.5.0 / go1.22.4)
If you want to update binaries, the following command.
$ gup update fyne_demo gup mimixbox
```
Like other subcommands, you can only check the specified binaries.
```shell
$ gup check lazygit mimixbox
check binary under $GOPATH/bin or $GOBIN
[1/2] github.com/jesseduffield/lazygit (Already up-to-date: v0.32.2 / go1.22.4)
[2/2] github.com/nao1215/mimixbox (current: v0.32.1, latest: v0.33.2 / go1.22.4)
If you want to update binaries, the following command.
$ gup update mimixbox
```
### Quiet output for large tool sets
`check` and `update` print every binary by default, which is noisy when you have many tools installed. Pass `--quiet` (`-q`) to suppress the up-to-date lines and show only the binaries that were updated (or have an update available) plus failures, followed by a one-line summary. Errors are always written to STDERR, so they stay visible. When `--json` is also given, `--quiet` is ignored and the full JSON array is printed.
```shell
$ gup update --quiet
github.com/nao1215/gup (v0.7.0 to v0.7.1)
gup: 1 updated, 8 up-to-date, 0 failed
$ gup check -q
github.com/nao1215/gup (current: v0.7.0, latest: v0.7.1 / go1.22.4)
If you want to update binaries, run the following command.
$ gup update gup
gup: 1 update available, 8 up-to-date, 0 failed
```
### Machine-readable JSON output (for scripting / CI)
`list`, `check`, and `update` accept `--json`, printing a JSON array instead of the human-readable output (which stays the default).
```shell
$ gup check --json
[
{
"name": "gup",
"import_path": "github.com/nao1215/gup",
"module_path": "github.com/nao1215/gup",
"channel": "latest",
"current_version": "v1.0.0",
"latest_version": "v1.1.0",
"current_go_version": "go1.22.4",
"installed_go_version": "go1.22.4",
"status": "update-available"
}
]
```
The fields are listed in the [reference](https://nao1215.github.io/gup/reference/#json-output-fields).
### Failure diagnostics / next-step hints
When `update` or `check` fails, gup turns the Go toolchain's cryptic output into a short, actionable next step printed on STDERR right after the error (and exposed as the `hint` field with `--json`):
```shell
$ gup update
gup:ERROR: [1/1] tool: can't install gup.test/moved/cmd/tool:
go: gup.test/moved/cmd/tool@latest: module gup.test/moved@latest found (v1.1.0), but does not contain package gup.test/moved/cmd/tool
gup:HINT : The module no longer provides this command at its import path. The project likely moved to a new major version (e.g. a `/v2` module path) or relocated the command; check its current install instructions and reinstall with the new path.
```
### Export/Import subcommand
Use export/import when you want to install the same Go binaries across multiple systems.
`gup.json` records each tool's import path, version, and update channel. The schema and the lookup order are in the [reference](https://nao1215.github.io/gup/reference/#gupjson).
```json
{
"schema_version": 1,
"packages": [
{
"name": "gal",
"import_path": "github.com/nao1215/gal/cmd/gal",
"version": "v1.1.1",
"channel": "latest"
},
{
"name": "posixer",
"import_path": "github.com/nao1215/posixer",
"version": "v0.1.0",
"channel": "main"
}
]
}
```
```shell
※ Environments A (e.g. ubuntu)
$ gup export
Export /home/nao/.config/gup/gup.json
※ Environments B (e.g. debian)
$ gup import
```
`export` can print config content to STDOUT by `--output`. `import` can read a specific file by `--file`.
```shell
※ Environments A (e.g. ubuntu)
$ gup export --output > gup.json
※ Environments B (e.g. debian)
$ gup import --file=gup.json
```
### Migrate binaries to a new $GOBIN
```shell
gup migrate BEFORE_PATH AFTER_PATH [BINARY...]
```
`gup migrate` reinstalls the Go binaries under `BEFORE_PATH` into `AFTER_PATH`, using the exact `import path@version` recorded in each binary's build info (it never silently upgrades to `@latest`). Internally it just sets `GOBIN` to `AFTER_PATH` and runs the normal `go install` path, so the binaries are rebuilt with the Go toolchain currently in use, or with the newer Go a binary was built with, as `update` does.
#### Why this is useful (e.g. with `mise`)
When you manage Go with [`mise`](https://mise.jdx.dev/), updating Go can change the real path of `$GOBIN` per Go version. As a result, tools you installed under the previous `$GOBIN` are no longer visible to the new Go. `gup migrate` lets you reinstall the same Go tool set from the old `$GOBIN` into the new one:
```shell
# Reinstall every go-install tool from the old GOBIN into the new GOBIN
$ gup migrate ~/.local/share/mise/installs/go/1.24.0/bin ~/.local/share/mise/installs/go/1.25.0/bin
# Migrate only specific binaries
$ gup migrate /old/gobin /new/gobin gopls air
```
`migrate` is add-only:
- It never deletes or cleans up files in `AFTER_PATH`.
- Binaries that already exist in `AFTER_PATH` are skipped by default. Use `--force` to reinstall over them.
- `AFTER_PATH` is created automatically when it does not exist.
- `BEFORE_PATH` and `AFTER_PATH` must be different directories.
### Generate man-pages (for linux, mac)
man subcommand generates man-pages under /usr/share/man/man1 by default. If `MANPATH` is set, gup writes to the `man1` directory under each entry instead, creating it when it does not exist yet. An unwritable target exits with a clear error.
```shell
$ sudo gup man
Generate /usr/share/man/man1/gup-bug-report.1.gz
Generate /usr/share/man/man1/gup-check.1.gz
Generate /usr/share/man/man1/gup-completion.1.gz
Generate /usr/share/man/man1/gup-export.1.gz
Generate /usr/share/man/man1/gup-import.1.gz
Generate /usr/share/man/man1/gup-list.1.gz
Generate /usr/share/man/man1/gup-man.1.gz
Generate /usr/share/man/man1/gup-migrate.1.gz
Generate /usr/share/man/man1/gup-remove.1.gz
Generate /usr/share/man/man1/gup-update.1.gz
Generate /usr/share/man/man1/gup-version.1.gz
Generate /usr/share/man/man1/gup.1.gz
```
### Generate shell completion file (for bash, zsh, fish, PowerShell)
`completion` prints completion scripts to STDOUT when you pass a shell name.
`--install` sets completion up for you instead, for the shells of the platform you are on.
```shell
$ gup completion bash > gup.bash
$ gup completion zsh > _gup
$ gup completion fish > gup.fish
$ gup completion powershell > gup.ps1
# Install files automatically to default user paths
$ gup completion --install
```
`--install` needs `HOME` (or `USERPROFILE` on Windows) and is safe to re-run. On Windows it sets up PowerShell; see the [install page](https://nao1215.github.io/gup/install/#shell-completion) for the paths it writes.
### Desktop notification
If you use gup with --notify option, gup command notify you on your desktop whether the update was successful or unsuccessful after the update was finished.
```shell
$ gup update --notify
```


### Disable colorized output
gup colorizes its output by default. To turn colors off, pass `--no-color` or set the `NO_COLOR` environment variable to a non-empty value (following the [NO_COLOR](https://no-color.org/) convention). This is useful when piping output, in CI logs, or with `NO_COLOR` set globally.
```shell
$ gup update --no-color
$ NO_COLOR=1 gup update
```
## gup vs. `go tool`
Go 1.24's built-in [`go tool`](https://go.dev/doc/modules/managing-dependencies#tools) manages tools scoped to a single project and recorded in that project's `go.mod`, so those tools exist only inside that module. gup manages the binaries installed system-wide under `$GOBIN`, the commands you run from any directory and keep alongside your dotfiles, optionally pinned to versions you depend on. Use `go tool` for per-project tooling and gup for your global toolbox.
Frequently Asked Questions
What is gup?
gup is a Package Management library for the Go programming language. Update binaries installed by "go install"
How do I install gup?
Install gup with the Go module system using `go get nao1215/gup`. Check the repository for the current installation instructions.
What category does gup belong to?
gup is listed under Package Management, specifically Package Management.