nao1215/gup

Fast manager for Go-installed binaries in $GOBIN: update, export/import, and migrate toolsets across machines

Go

615

1,265 commits

updated Oct 16, 2026

See the code

README

All Contributors

Mentioned in Awesome Go reviewdog Coverage tested with atago measured with himorime Go Reference GitHub GitHub Downloads (all assets, all releases) OpenSSF Scorecard

sample

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.

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:

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:

brew install nao1215/tap/gup

Use winget (Windows)

winget install --id nao1215.gup

Use mise-en-place

mise use -g gup@latest

Use nix (Nix profile)

nix profile install nixpkgs#gogup

Use aqua

gup is registered in the aqua standard registry. Add it to your aqua.yaml:

aqua g -i nao1215/gup

Use the AUR (Arch Linux)

Two community-maintained packages are available: gup builds from source, gup-bin installs the release binary.

paru -S gup      # or: yay -S gup
paru -S gup-bin  # prebuilt binary

Install from Package or Binary

The release page 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:

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

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:

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, which checks the provenance signature against the Sigstore transparency log:

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.

$ gup --version
$ gup version

Update all binaries

gup update updates every binary under $GOBIN, in parallel.

update

Update the specified binary

If you want to update only the specified binaries, you specify multiple command names separated by space.

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

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

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

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

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

$ 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. list

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.

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

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

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

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

$ 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).

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

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

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

{
  "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"
    }
  ]
}
※ 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.

※ 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

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

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

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

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

$ gup update --notify

success warning

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 convention). This is useful when piping output, in CI logs, or with NO_COLOR set globally.

$ gup update --no-color
$ NO_COLOR=1 gup update

gup vs. go tool

Go 1.24's built-in go tool 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.

Feature comparison

Featuregupgo-global-updatego install loop
Parallel updateYesNoManual
Per-package update channels (latest/main/master)YesNoManual
Version pinning / lockYesNoManual
Export/import tool setYesNoManual
Migrate binaries to a new $GOBINYesNoManual
Machine-readable JSON output (--json)YesNoNo
Shell completion generation/installYesNoNo
update reinstalls up-to-date binariesNoYesYes
migrate --force reinstalls when the target already existsYesNoManual
Failure diagnostics / next-step hintsYesYesNo
NO_COLOR supportYesYes—

gup updates binaries in parallel; go-global-update and a go install loop update them one after another. How long each takes to update the same 30 binaries is measured end to end with himorime, and the latest figures, with the machine and the versions they were measured on, are in bench/README.md.

Integrations

gup runs inside higher-level upgrade tools. Nothing extra is needed on the gup side.

  • Topgrade has run gup update in its Go step since v10.2.0. Binaries can be skipped with gup_exclude (v17.3.0 and later).
  • Meta Package Manager lists, checks, removes, and upgrades the binaries gup manages. The support ships in its upcoming v8.0.0.

The exit codes, --json fields and text output these tools and a number of dotfiles scripts rely on are pinned as end-to-end contracts in e2e/atago/consumers.atago.yaml, so a change that would break a caller fails CI first.

FAQ

gup fails with fatal: not a git repository

You are probably on oh-my-zsh, which ships a gup alias for git pull --rebase that shadows this command (#16, #204). Remove or rename that alias, or run gup with a leading backslash to bypass it:

$ \gup update

Contributing

First off, thanks for taking the time to contribute! ❤️ See CONTRIBUTING.md for more information. Developer workflow, quality checklist, and tool management are documented in CONTRIBUTING.md. Contributions are not only related to development. For example, GitHub Star motivates me to develop!

Contact

If you would like to send comments such as "find a bug" or "request for additional features" to the developer, please use one of the following contacts.

You can use the bug-report subcommand to send a bug report.

$ gup bug-report
※ Open GitHub issue page by your default browser

LICENSE

The gup project is licensed under the terms of the Apache License 2.0.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

cli
command-line-tool
cross-platform
developer-tools
dotfiles
go
gobin
go-install
golang
golang-tools
updater

nao1215/gup

Fast manager for Go-installed binaries in $GOBIN: update, export/import, and migrate toolsets across machines

Go

615

1,265 commits

updated Oct 16, 2026

See the code

README

All Contributors

Mentioned in Awesome Go reviewdog Coverage tested with atago measured with himorime Go Reference GitHub GitHub Downloads (all assets, all releases) OpenSSF Scorecard

sample

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.

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:

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:

brew install nao1215/tap/gup

Use winget (Windows)

winget install --id nao1215.gup

Use mise-en-place

mise use -g gup@latest

Use nix (Nix profile)

nix profile install nixpkgs#gogup

Use aqua

gup is registered in the aqua standard registry. Add it to your aqua.yaml:

aqua g -i nao1215/gup

Use the AUR (Arch Linux)

Two community-maintained packages are available: gup builds from source, gup-bin installs the release binary.

paru -S gup      # or: yay -S gup
paru -S gup-bin  # prebuilt binary

Install from Package or Binary

The release page 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:

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

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:

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, which checks the provenance signature against the Sigstore transparency log:

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.

$ gup --version
$ gup version

Update all binaries

gup update updates every binary under $GOBIN, in parallel.

update

Update the specified binary

If you want to update only the specified binaries, you specify multiple command names separated by space.

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

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

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

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

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

$ 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. list

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.

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

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

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

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

$ 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).

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

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

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

{
  "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"
    }
  ]
}
※ 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.

※ 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

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

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

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

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

$ gup update --notify

success warning

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 convention). This is useful when piping output, in CI logs, or with NO_COLOR set globally.

$ gup update --no-color
$ NO_COLOR=1 gup update

gup vs. go tool

Go 1.24's built-in go tool 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.

Feature comparison

Featuregupgo-global-updatego install loop
Parallel updateYesNoManual
Per-package update channels (latest/main/master)YesNoManual
Version pinning / lockYesNoManual
Export/import tool setYesNoManual
Migrate binaries to a new $GOBINYesNoManual
Machine-readable JSON output (--json)YesNoNo
Shell completion generation/installYesNoNo
update reinstalls up-to-date binariesNoYesYes
migrate --force reinstalls when the target already existsYesNoManual
Failure diagnostics / next-step hintsYesYesNo
NO_COLOR supportYesYes—

gup updates binaries in parallel; go-global-update and a go install loop update them one after another. How long each takes to update the same 30 binaries is measured end to end with himorime, and the latest figures, with the machine and the versions they were measured on, are in bench/README.md.

Integrations

gup runs inside higher-level upgrade tools. Nothing extra is needed on the gup side.

  • Topgrade has run gup update in its Go step since v10.2.0. Binaries can be skipped with gup_exclude (v17.3.0 and later).
  • Meta Package Manager lists, checks, removes, and upgrades the binaries gup manages. The support ships in its upcoming v8.0.0.

The exit codes, --json fields and text output these tools and a number of dotfiles scripts rely on are pinned as end-to-end contracts in e2e/atago/consumers.atago.yaml, so a change that would break a caller fails CI first.

FAQ

gup fails with fatal: not a git repository

You are probably on oh-my-zsh, which ships a gup alias for git pull --rebase that shadows this command (#16, #204). Remove or rename that alias, or run gup with a leading backslash to bypass it:

$ \gup update

Contributing

First off, thanks for taking the time to contribute! ❤️ See CONTRIBUTING.md for more information. Developer workflow, quality checklist, and tool management are documented in CONTRIBUTING.md. Contributions are not only related to development. For example, GitHub Star motivates me to develop!

Contact

If you would like to send comments such as "find a bug" or "request for additional features" to the developer, please use one of the following contacts.

You can use the bug-report subcommand to send a bug report.

$ gup bug-report
※ Open GitHub issue page by your default browser

LICENSE

The gup project is licensed under the terms of the Apache License 2.0.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

cli
command-line-tool
cross-platform
developer-tools
dotfiles
go
gobin
go-install
golang
golang-tools
updater

Project health

Overall

8.4/10

Maintained

10/10

Code review

0/10

Contributors

10/10
OpenSSF Scorecard

Latest release

v1.10.3 · Sep 28, 2026

Releases this year

30

Significant stargazers

Ludovic Fernandez

742 followers · starred Dec 2024

James Sumners

414 followers · starred May 2022

apocelipes

109 followers · starred Feb 2022

Gabe Cook

190 followers · starred Aug 2026