go-github-selfupdate/README.md

372 lines
12 KiB
Markdown
Raw Normal View History

2017-12-25 07:32:28 +00:00
Self-Update Mechanism for Go Commands using GitHub
==================================================
[![GoDoc Badge][]][GoDoc]
[![TravisCI Status][]][TravisCI]
[![AppVeyor Status][]][AppVeyor]
[![Codecov Status][]][Codecov]
2017-12-25 07:32:28 +00:00
[go-github-selfupdate][] is a Go library to provide self-update mechanism to command line tools.
2018-02-11 14:36:50 +00:00
Go does not provide the way to install/update the stable version of tools. By default, Go command line
tools are updated:
2017-12-25 07:32:28 +00:00
- using `go get -u` (updating to HEAD)
- using system's package manager (depending on the platform)
2017-12-30 02:45:47 +00:00
- downloading executables from GitHub release page manually
2017-12-25 07:32:28 +00:00
By using this library, you will get 4th choice:
2017-12-30 02:45:47 +00:00
- from your command line tool directly (and automatically)
2017-12-25 07:32:28 +00:00
2018-02-11 14:36:50 +00:00
[go-github-selfupdate][] detects the information of the latest release via [GitHub Releases API][] and
check the current version.
If newer version than itself is detected, it downloads released binary from GitHub and replaces
itself.
2017-12-25 07:32:28 +00:00
2017-12-30 02:45:47 +00:00
- Automatically detects the latest version of released binary on GitHub
- Retrieve the proper binary for the OS and arch where the binary is running
- Update the binary with rollback support on failure
- Tested on Linux, macOS and Windows (using Travis CI and AppVeyor)
2017-12-31 03:14:17 +00:00
- Many archive and compression formats are supported (zip, tar, gzip, xzip)
- Support private repositories
- Support [GitHub Enterprise][]
2017-12-25 07:32:28 +00:00
2017-12-30 08:40:59 +00:00
And small wrapper CLIs are provided:
2017-12-30 07:14:06 +00:00
- [detect-latest-release](./cmd/detect-latest-release): Detect the latest release of given GitHub repository from command line
2017-12-30 08:40:59 +00:00
- [go-get-release](./cmd/go-get-release): Like `go get`, but install release binary from GitHub instead
2017-12-30 07:14:06 +00:00
2017-12-25 07:32:28 +00:00
[go-github-selfupdate]: https://github.com/rhysd/go-github-selfupdate
[GitHub Releases API]: https://developer.github.com/v3/repos/releases/
2017-12-30 02:45:47 +00:00
## Try Out Example
2017-12-30 05:27:31 +00:00
Example to understand what this library does is prepared as [CLI](./cmd/selfupdate-example/main.go).
Install it at first.
```
$ go get -u github.com/rhysd/go-github-selfupdate/tree/master/cmd/selfupdate-example
```
And check the version by `-version`. `-help` flag is also available to know all flags.
```
$ selfupdate-example -version
```
It should show `v1.2.3`.
Then run `-selfupdate`
```
$ selfupdate-example -selfupdate
```
It should replace itself and finally shows a message containing release notes.
2018-02-11 14:36:50 +00:00
Please check the binary version is updated to `v1.2.4` with `-version`. The binary is up-to-date.
So running `-selfupdate` again only shows 'Current binary is the latest version'.
2017-12-30 05:27:31 +00:00
2017-12-30 06:42:13 +00:00
### Real World Examples
Following tools are using this library.
- [dot-github](https://github.com/rhysd/dot-github)
- [dotfiles](https://github.com/rhysd/dotfiles)
- [github-clone-all](https://github.com/rhysd/github-clone-all)
2018-02-06 06:48:03 +00:00
- [pythonbrew](https://github.com/utahta/pythonbrew)
- [akashic](https://github.com/cowlick/akashic)
- [butler](https://github.com/netzkern/butler)
2017-12-30 02:45:47 +00:00
2017-12-25 07:32:28 +00:00
## Usage
2017-12-30 02:45:47 +00:00
### Code Usage
2017-12-25 07:32:28 +00:00
It provides `selfupdate` package.
2017-12-30 04:12:56 +00:00
- `selfupdate.UpdateSelf()`: Detect the latest version of itself and run self update.
- `selfupdate.UpdateCommand()`: Detect the latest version of given repository and update given command.
- `selfupdate.DetectLatest()`: Detect the latest version of given repository.
- `selfupdate.UpdateTo()`: Update given command to the binary hosted on given URL.
- `selfupdate.Updater`: Context manager of self-upadte process. If you want to customize some behavior
of self-update (e.g. specify API token, use GitHub Enterprise, ...), please make an instance of
`Updater` and use its methods.
2017-12-30 04:12:56 +00:00
2017-12-30 02:45:47 +00:00
Following is the easiest way to use this package.
2017-12-25 07:32:28 +00:00
```go
import (
"log"
2017-12-30 04:12:56 +00:00
"github.com/blang/semver"
2017-12-26 02:45:08 +00:00
"github.com/rhysd/go-github-selfupdate/selfupdate"
2017-12-25 07:32:28 +00:00
)
2017-12-30 02:45:47 +00:00
const version = "1.2.3"
func doSelfUpdate() {
v := semver.MustParse(version)
latest, err := selfupdate.UpdateSelf(v, "myname/myrepo")
2017-12-25 07:32:28 +00:00
if err != nil {
2017-12-30 02:45:47 +00:00
log.Println("Binary update failed:", err)
2017-12-25 07:32:28 +00:00
return
}
2017-12-30 02:45:47 +00:00
if latest.Version.Equals(v) {
// latest version is the same as current version. It means current binary is up to date.
2017-12-25 07:32:28 +00:00
log.Println("Current binary is the latest version", version)
} else {
2017-12-30 02:45:47 +00:00
log.Println("Successfully updated to version", latest.Version)
2017-12-30 04:12:56 +00:00
log.Println("Release note:\n", latest.ReleaseNotes)
2017-12-25 07:32:28 +00:00
}
}
```
2017-12-30 04:12:56 +00:00
Following asks user to update or not.
```go
import (
"bufio"
"github.com/blang/semver"
"github.com/rhysd/go-github-selfupdate/selfupdate"
"log"
"os"
)
const version = "1.2.3"
func confirmAndSelfUpdate() {
latest, found, err := selfupdate.DetectLatest("owner/repo")
if err != nil {
log.Println("Error occurred while detecting version:", err)
return
}
v := semver.MustParse(version)
if !found || latest.Version.Equals(v) {
log.Println("Current version is the latest")
return
}
fmt.Print("Do you want to update to", latest.Version, "? (y/n): ")
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil || (input != "y\n" && input != "n\n") {
log.Println("Invalid input")
return
}
if input == "n\n" {
return
}
if err := selfupdate.UpdateTo(latest.AssetURL, os.Args[0]); err != nil {
log.Println("Error occurred while updating binary:", err)
return
}
log.Println("Successfully updated to version", latest.Version)
}
```
2017-12-25 07:32:28 +00:00
If GitHub API token is set to `[token]` section in `gitconfig` or `$GITHUB_TOKEN` environment variable,
this library will use it to call GitHub REST API. It's useful when reaching rate limits or when using
this library with private repositories.
2017-12-25 07:32:28 +00:00
Please see [the documentation page][GoDoc] for more detail.
This library should work with [GitHub Enterprise][]. To configure API base URL, please setup `Updater`
instance and use its method instead (Actually all functions above are just a shortcuts of methods of
`Updater` instance).
Following is an example of usage with GitHub Enterprise.
```go
import (
"log"
"github.com/blang/semver"
"github.com/rhysd/go-github-selfupdate/selfupdate"
)
const version = "1.2.3"
func doSelfUpdate(token string) {
v := semver.MustParse(version)
up, err := selfupdate.NewUpdater(selfupdate.Config{
APIToken: token,
EnterpriseBaseURL: "https://github.your.company.com/api/v3",
})
latest, err := up.UpdateSelf(v, "myname/myrepo")
if err != nil {
log.Println("Binary update failed:", err)
return
}
if latest.Version.Equals(v) {
// latest version is the same as current version. It means current binary is up to date.
log.Println("Current binary is the latest version", version)
} else {
log.Println("Successfully updated to version", latest.Version)
log.Println("Release note:\n", latest.ReleaseNotes)
}
}
```
If `APIToken` field is not given, it tries to retrieve API token from `[token]` section of `.gitconfig`
or `$GITHUB_TOKEN` environment variable. If no token is found, it raises an error because GitHub Enterprise
API does not work without authentication.
If your GitHub Enterprise instance's upload URL is different from the base URL, please also set `EnterpriseUploadURL`
field
2017-12-25 07:32:28 +00:00
### Naming Rules of Released Binaries
go-github-selfupdate assumes that released binaries are put for each combination of platforms and archs.
2017-12-30 02:45:47 +00:00
Binaries for each platform can be easily built using tools like [gox][]
2017-12-25 07:32:28 +00:00
2017-12-30 02:45:47 +00:00
You need to put the binaries with the following format.
```
{cmd}_{goos}_{goarch}{.ext}
```
`{cmd}` is a name of command.
`{goos}` and `{goarch}` are the platform and the arch type of the binary.
2017-12-31 03:14:17 +00:00
`{.ext}` is a file extension. go-github-selfupdate supports `.zip`, `.gzip`, `.tar.gz` and `.tar.xz`.
2017-12-30 02:45:47 +00:00
You can also use blank and it means binary is not compressed.
If you compress binary, uncompressed directory or file must contain the executable named `{cmd}`.
And you can also use `-` for separator instead of `_` if you like.
For example, if your command name is `foo-bar`, one of followings is expected to be put in release
page on GitHub as binary for platform `linux` and arch `amd64`.
2017-12-25 07:32:28 +00:00
- `foo-bar_linux_amd64` (executable)
2017-12-30 02:45:47 +00:00
- `foo-bar_linux_amd64.zip` (zip file containing `foo-bar`)
- `foo-bar_linux_amd64.tar.gz` (tar file containing `foo-bar`)
2017-12-31 03:14:17 +00:00
- `foo-bar_linux_amd64.xz` (xzip file of the executable `foo-bar`)
2017-12-30 02:45:47 +00:00
- `foo-bar-linux-amd64.tar.gz` (`-` is also ok for separator)
2017-12-30 03:52:59 +00:00
To archive the executable directly on Windows, `.exe` can be added before file extension like
`foo-bar_windows_amd64.exe.zip`.
2017-12-30 02:45:47 +00:00
[gox]: https://github.com/mitchellh/gox
2017-12-25 07:32:28 +00:00
2017-12-30 02:45:47 +00:00
### Naming Rules of Versions (=Git Tags)
2017-12-25 07:32:28 +00:00
2017-12-30 02:45:47 +00:00
go-github-selfupdate searches binaries' versions via Git tag names (not a release title).
When your tool's version is `1.2.3`, you should use the version number for tag of the Git
repository (i.e. `1.2.3` or `v1.2.3`).
This library assumes you adopt [semantic versioning][]. It is necessary for comparing versions
systematically.
Prefix before version number `\d+\.\d+\.\d+` is automatically omitted. For example, `ver1.2.3` or
`release-1.2.3` are also ok.
Tags which don't contain a version number are ignored (i.e. `nightly`). And releases marked as `pre-release`
are also ignored.
2017-12-25 07:32:28 +00:00
2017-12-30 03:31:07 +00:00
[semantic versioning]: https://semver.org/
2017-12-25 07:32:28 +00:00
### Structure of Releases
In summary, structure of releases on GitHub looks like:
- `v1.2.0`
2017-12-30 02:45:47 +00:00
- `foo-bar-linux-amd64.tar.gz`
- `foo-bar-linux-386.tar.gz`
- `foo-bar-darwin-amd64.tar.gz`
2017-12-25 07:32:28 +00:00
- `foo-bar-windows-amd64.zip`
- ... (Other binaries for v1.2.0)
- `v1.1.3`
2017-12-30 02:45:47 +00:00
- `foo-bar-linux-amd64.tar.gz`
- `foo-bar-linux-386.tar.gz`
- `foo-bar-darwin-amd64.tar.gz`
2017-12-25 07:32:28 +00:00
- `foo-bar-windows-amd64.zip`
- ... (Other binaries for v1.1.3)
- ... (older versions)
2018-02-11 14:36:50 +00:00
Tags which don't contain a version number are ignored (i.e. `nightly`). And releases marked
as `pre-release` are also ignored.
2017-12-30 02:45:47 +00:00
2018-02-11 14:36:50 +00:00
### Development
#### Running tests
All library sources are put in `/selfupdate` directory. So you can run tests as following
at the top of the repository:
```
$ go test -v ./selfupdate
```
Some tests are not run without setting a GitHub API token because they call GitHub API too many times.
To run them, please generate an API token and set it to an environment variable.
```
$ export GITHUB_TOKEN="{token generated by you}"
$ go test -v ./selfupdate
```
Above command run almost all tests and it's enough to check the behavior before creating a pull request.
Some tests are still not tested because they depend on my personal API access token, though; for repositories
on GitHub Enterprise or private repositories on GitHub.
#### Debugging
2017-12-30 02:45:47 +00:00
This library can output logs for debugging. By default, logger is disabled.
You can enable the logger by following and can know the details of the self update.
```go
selfupdate.EnableLog()
```
2017-12-25 07:32:28 +00:00
2018-02-11 14:36:50 +00:00
#### CI
Tests run on CIs (Travis CI, Appveyor) are run with the token I generated. However, because of security
reason, it is not used for the tests for pull requests. In the tests, a GitHub API token is not set and
API rate limit is often exceeding. So please ignore the test failures on creating a pull request.
2017-12-25 07:32:28 +00:00
## Dependencies
2017-12-30 02:45:47 +00:00
This library utilizes [go-github][] to retrieve the information of releases and [go-update][] to replace
current binary and [semver][] to compare versions.
2017-12-25 07:32:28 +00:00
> Copyright (c) 2013 The go-github AUTHORS. All rights reserved.
> Copyright 2015 Alan Shreve
> Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
[go-github]: https://github.com/google/go-github
2017-12-26 02:48:09 +00:00
[go-update]: https://github.com/inconshreveable/go-update
2017-12-25 07:32:28 +00:00
[semver]: https://github.com/blang/semver
2017-12-30 02:45:47 +00:00
2017-12-30 17:19:45 +00:00
## What is different from [tj/go-update][]?
2017-12-30 02:45:47 +00:00
2017-12-30 03:13:58 +00:00
This library goal is the same as tj/go-update, but it's different in following points.
tj/go-update:
- does not support Windows
- only allows `v` for version prefix
- does not ignore pre-release
- has [only a few tests](https://github.com/tj/go-update/blob/master/update_test.go)
2017-12-30 17:19:45 +00:00
- supports Apex store for putting releases
2017-12-30 02:45:47 +00:00
2017-12-30 03:55:12 +00:00
[tj/go-update]: https://github.com/tj/go-update
## License
Distributed under the [MIT License](LICENSE)
2017-12-30 02:45:47 +00:00
[GoDoc Badge]: https://godoc.org/github.com/rhysd/go-github-selfupdate/selfupdate?status.svg
2017-12-30 03:31:07 +00:00
[GoDoc]: https://godoc.org/github.com/rhysd/go-github-selfupdate/selfupdate
[TravisCI Status]: https://travis-ci.org/rhysd/go-github-selfupdate.svg?branch=master
[TravisCI]: https://travis-ci.org/rhysd/go-github-selfupdate
[AppVeyor Status]: https://ci.appveyor.com/api/projects/status/1tpyd9q9tw3ime5u/branch/master?svg=true
[AppVeyor]: https://ci.appveyor.com/project/rhysd/go-github-selfupdate/branch/master
[Codecov Status]: https://codecov.io/gh/rhysd/go-github-selfupdate/branch/master/graph/badge.svg
[Codecov]: https://codecov.io/gh/rhysd/go-github-selfupdate
[GitHub Enterprise]: https://enterprise.github.com/home