github.com/creativeprojects/go-selfupdate@v1.2.0/README.md (about) 1 Self-Update library for Github, Gitea and Gitlab hosted applications in Go 2 ============================================================== 3 4 [![Godoc reference](https://godoc.org/github.com/creativeprojects/go-selfupdate?status.svg)](http://godoc.org/github.com/creativeprojects/go-selfupdate) 5 [![Build](https://github.com/creativeprojects/go-selfupdate/workflows/Build/badge.svg)](https://github.com/creativeprojects/go-selfupdate/actions) 6 [![codecov](https://codecov.io/gh/creativeprojects/go-selfupdate/branch/main/graph/badge.svg?token=3FejM0fkw2)](https://codecov.io/gh/creativeprojects/go-selfupdate) 7 8 <!--ts--> 9 * [Self\-Update library for Github, Gitea and Gitlab hosted applications in Go](#self-update-library-for-github-gitea-and-gitlab-hosted-applications-in-go) 10 * [Introduction](#introduction) 11 * [Example](#example) 12 * [Upgrade from v0\+ to v1](#upgrade-from-v0-to-v1) 13 * [Repository](#repository) 14 * [ParseSlug](#parseslug) 15 * [NewRepositorySlug](#newrepositoryslug) 16 * [NewRepositoryID (GitLab only)](#newrepositoryid-gitlab-only) 17 * [Context](#context) 18 * [Package functions](#package-functions) 19 * [Methods on Source interface](#methods-on-source-interface) 20 * [Methods on Updater struct](#methods-on-updater-struct) 21 * [Naming Rules of Released Binaries](#naming-rules-of-released-binaries) 22 * [Naming Rules of Versions (=Git Tags)](#naming-rules-of-versions-git-tags) 23 * [Structure of Releases](#structure-of-releases) 24 * [Special case for ARM architecture](#special-case-for-arm-architecture) 25 * [Hash or Signature Validation](#hash-or-signature-validation) 26 * [SHA256](#sha256) 27 * [ECDSA](#ecdsa) 28 * [Using a single checksum file for all your assets](#using-a-single-checksum-file-for-all-your-assets) 29 * [Other providers than Github](#other-providers-than-github) 30 * [GitLab](#gitlab) 31 * [Example:](#example-1) 32 * [Copyright](#copyright) 33 34 <!--te--> 35 36 # Introduction 37 38 go-selfupdate detects the information of the latest release via a source provider and 39 checks the current version. If a newer version than itself is detected, it downloads the released binary from 40 the source provider and replaces itself. 41 42 - Automatically detect the latest version of released binary on the source provider 43 - Retrieve the proper binary for the OS and arch where the binary is running 44 - Update the binary with rollback support on failure 45 - Tested on Linux, macOS and Windows 46 - Many archive and compression formats are supported (zip, tar, gzip, xzip, bzip2) 47 - Support private repositories 48 - Support hash, signature validation 49 50 Two source providers are available: 51 - GitHub 52 - Gitea 53 - Gitlab 54 55 This library started as a fork of https://github.com/rhysd/go-github-selfupdate. A few things have changed from the original implementation: 56 - don't expose an external semver.Version type, but provide the same functionality through the API: LessThan, Equal and GreaterThan 57 - use an interface to send logs (compatible with standard log.Logger) 58 - able to detect different ARM CPU architectures (the original library wasn't working on my different versions of raspberry pi) 59 - support for assets compressed with bzip2 (.bz2) 60 - can use a single file containing the sha256 checksums for all the files (one per line) 61 - separate the provider and the updater, so we can add more providers (Github, Gitea, Gitlab, etc.) 62 - return well defined wrapped errors that can be checked with `errors.Is(err error, target error)` 63 64 # Example 65 66 Here's an example how to use the library for an application to update itself 67 68 ```go 69 func update(version string) error { 70 latest, found, err := selfupdate.DetectLatest(context.Background(), selfupdate.ParseSlug("creativeprojects/resticprofile")) 71 if err != nil { 72 return fmt.Errorf("error occurred while detecting version: %w", err) 73 } 74 if !found { 75 return fmt.Errorf("latest version for %s/%s could not be found from github repository", runtime.GOOS, runtime.GOARCH) 76 } 77 78 if latest.LessOrEqual(version) { 79 log.Printf("Current version (%s) is the latest", version) 80 return nil 81 } 82 83 exe, err := os.Executable() 84 if err != nil { 85 return errors.New("could not locate executable path") 86 } 87 if err := selfupdate.UpdateTo(context.Background(), latest.AssetURL, latest.AssetName, exe); err != nil { 88 return fmt.Errorf("error occurred while updating binary: %w", err) 89 } 90 log.Printf("Successfully updated to version %s", latest.Version()) 91 return nil 92 } 93 ``` 94 95 # Upgrade from v0+ to v1 96 97 Version v1+ has a **stable** API. It is slightly different from the API of versions 0+. 98 99 ## Repository 100 101 Some functions needed a couple `owner`/`repo` and some other a single string called `slug`. These have been replaced by a `Repository`. 102 103 Two constructors are available: 104 105 ### ParseSlug 106 107 Parses a *slug* string like `owner/repository_name` 108 109 ```go 110 func ParseSlug(slug string) RepositorySlug 111 ``` 112 113 ### NewRepositorySlug 114 115 Creates a repository from both owner and repo strings 116 117 ```go 118 func NewRepositorySlug(owner, repo string) RepositorySlug 119 ``` 120 121 ### NewRepositoryID (GitLab only) 122 123 GitLab can also refer to a repository via its internal ID. This constructor can be used with a numeric repository ID. 124 125 ```go 126 func NewRepositoryID(id int) RepositoryID 127 ``` 128 129 ### Context 130 131 All methods are now accepting a `context` as their first parameter. You can use it to cancel a long running operation. 132 133 ## Package functions 134 135 | v0 | v1 | 136 |----|----| 137 | UpdateTo(assetURL, assetFileName, cmdPath string) error | UpdateTo(ctx context.Context, assetURL, assetFileName, cmdPath string) error | 138 | DetectLatest(slug string) (*Release, bool, error) | DetectLatest(ctx context.Context, repository Repository) (*Release, bool, error) | 139 | DetectVersion(slug string, version string) (*Release, bool, error) | DetectVersion(ctx context.Context, repository Repository, version string) (*Release, bool, error) | 140 | UpdateCommand(cmdPath string, current string, slug string) (*Release, error) | UpdateCommand(ctx context.Context, cmdPath string, current string, repository Repository) (*Release, error) | 141 | UpdateSelf(current string, slug string) (*Release, error) | UpdateSelf(ctx context.Context, current string, repository Repository) (*Release, error) | 142 143 ## Methods on Source interface 144 145 | v0 | v1 | 146 |----|----| 147 | ListReleases(owner, repo string) ([]SourceRelease, error) | ListReleases(ctx context.Context, repository Repository) ([]SourceRelease, error) | 148 | DownloadReleaseAsset(owner, repo string, releaseID, id int64) (io.ReadCloser, error) | DownloadReleaseAsset(ctx context.Context, rel *Release, assetID int64) (io.ReadCloser, error) | 149 150 ## Methods on Updater struct 151 152 | v0 | v1 | 153 |----|----| 154 | DetectLatest(slug string) (release *Release, found bool, err error) | DetectLatest(ctx context.Context, repository Repository) (release *Release, found bool, err error) | 155 | DetectVersion(slug string, version string) (release *Release, found bool, err error) | DetectVersion(ctx context.Context, repository Repository, version string) (release *Release, found bool, err error) | 156 | UpdateCommand(cmdPath string, current string, slug string) (*Release, error) | UpdateCommand(ctx context.Context, cmdPath string, current string, repository Repository) (*Release, error) | 157 | UpdateSelf(current string, slug string) (*Release, error) | UpdateSelf(ctx context.Context, current string, repository Repository) (*Release, error) | 158 | UpdateTo(rel *Release, cmdPath string) error | UpdateTo(ctx context.Context, rel *Release, cmdPath string) error | 159 160 161 # Naming Rules of Released Binaries 162 163 go-selfupdate assumes that released binaries are put for each combination of platforms and architectures. 164 Binaries for each platform can be easily built using tools like [goreleaser][] 165 166 You need to put the binaries with the following format. 167 168 ``` 169 {cmd}_{goos}_{goarch}{.ext} 170 ``` 171 172 `{cmd}` is a name of command. 173 `{goos}` and `{goarch}` are the platform and the arch type of the binary. 174 `{.ext}` is a file extension. go-selfupdate supports `.zip`, `.gzip`, `.bz2`, `.tar.gz` and `.tar.xz`. 175 You can also use blank and it means binary is not compressed. 176 177 If you compress binary, uncompressed directory or file must contain the executable named `{cmd}`. 178 179 And you can also use `-` for separator instead of `_` if you like. 180 181 For example, if your command name is `foo-bar`, one of followings is expected to be put in release 182 page on GitHub as binary for platform `linux` and arch `amd64`. 183 184 - `foo-bar_linux_amd64` (executable) 185 - `foo-bar_linux_amd64.zip` (zip file) 186 - `foo-bar_linux_amd64.tar.gz` (tar file) 187 - `foo-bar_linux_amd64.xz` (xzip file) 188 - `foo-bar-linux-amd64.tar.gz` (`-` is also ok for separator) 189 190 If you compress and/or archive your release asset, it must contain an executable named one of followings: 191 192 - `foo-bar` (only command name) 193 - `foo-bar_linux_amd64` (full name) 194 - `foo-bar-linux-amd64` (`-` is also ok for separator) 195 196 To archive the executable directly on Windows, `.exe` can be added before file extension like 197 `foo-bar_windows_amd64.exe.zip`. 198 199 [goreleaser]: https://github.com/goreleaser/goreleaser/ 200 201 202 # Naming Rules of Versions (=Git Tags) 203 204 go-selfupdate searches binaries' versions via Git tag names (not a release title). 205 When your tool's version is `1.2.3`, you should use the version number for tag of the Git 206 repository (i.e. `1.2.3` or `v1.2.3`). 207 208 This library assumes you adopt [semantic versioning][]. It is necessary for comparing versions 209 systematically. 210 211 Prefix before version number `\d+\.\d+\.\d+` is automatically omitted. For example, `ver1.2.3` or 212 `release-1.2.3` are also ok. 213 214 Tags which don't contain a version number are ignored (i.e. `nightly`). And releases marked as `pre-release` 215 are also ignored. 216 217 [semantic versioning]: https://semver.org/ 218 219 220 # Structure of Releases 221 222 In summary, structure of releases on GitHub looks like: 223 224 - `v1.2.0` 225 - `foo-bar-linux-amd64.tar.gz` 226 - `foo-bar-linux-386.tar.gz` 227 - `foo-bar-darwin-amd64.tar.gz` 228 - `foo-bar-windows-amd64.zip` 229 - ... (Other binaries for v1.2.0) 230 - `v1.1.3` 231 - `foo-bar-linux-amd64.tar.gz` 232 - `foo-bar-linux-386.tar.gz` 233 - `foo-bar-darwin-amd64.tar.gz` 234 - `foo-bar-windows-amd64.zip` 235 - ... (Other binaries for v1.1.3) 236 - ... (older versions) 237 238 # Special case for ARM architecture 239 240 If you're using [goreleaser](https://github.com/goreleaser/goreleaser/) targeting ARM CPUs, it will use the version of the ARM architecture as a name: 241 - `armv5` 242 - `armv6` 243 - `armv7` 244 245 go-selfupdate will check which architecture was used to build the current binary. Please note it's **not detecting the hardware**, but the binary target instead. If you run an `armv6` binary on an `armv7` CPU, it will keep `armv6` as a target. 246 247 As a rule, it will search for a binary with the same architecture first, then try the architectures below if available, and as a last resort will try a simple `arm` architecture tag. 248 249 So if you're running a `armv6` binary, it will try these targets in order: 250 - `armv6` 251 - `armv5` 252 - `arm` 253 254 More information on targeting ARM cpu can be found here: [GoArm](https://github.com/golang/go/wiki/GoArm) 255 256 # Hash or Signature Validation 257 258 go-selfupdate supports hash or signature validation of the downloaded files. It comes 259 with support for sha256 hashes or ECDSA signatures. If you need something different, 260 you can implement the `Validator` interface with your own validation mechanism: 261 262 ```go 263 // Validator represents an interface which enables additional validation of releases. 264 type Validator interface { 265 // Validate validates release bytes against an additional asset bytes. 266 // See SHAValidator or ECDSAValidator for more information. 267 Validate(filename string, release, asset []byte) error 268 // GetValidationAssetName returns the additional asset name containing the validation checksum. 269 // The asset containing the checksum can be based on the release asset name 270 // Please note if the validation file cannot be found, the DetectLatest and DetectVersion methods 271 // will fail with a wrapped ErrValidationAssetNotFound error 272 GetValidationAssetName(releaseFilename string) string 273 } 274 ``` 275 276 ## SHA256 277 278 To verify the integrity by SHA256, generate a hash sum and save it within a file which has the 279 same naming as original file with the suffix `.sha256`. 280 For e.g. use sha256sum, the file `selfupdate/testdata/foo.zip.sha256` is generated with: 281 ```shell 282 sha256sum foo.zip > foo.zip.sha256 283 ``` 284 285 ## ECDSA 286 To verify the signature by ECDSA generate a signature and save it within a file which has the 287 same naming as original file with the suffix `.sig`. 288 For e.g. use openssl, the file `selfupdate/testdata/foo.zip.sig` is generated with: 289 ```shell 290 openssl dgst -sha256 -sign Test.pem -out foo.zip.sig foo.zip 291 ``` 292 293 go-selfupdate makes use of go internal crypto package. Therefore the private key 294 has to be compatible with FIPS 186-3. 295 296 ## Using a single checksum file for all your assets 297 298 Tools like [goreleaser][] produce a single checksum file for all your assets. A Validator is provided out of the box for this case: 299 300 ```go 301 updater, _ := NewUpdater(Config{Validator: &ChecksumValidator{UniqueFilename: "checksums.txt"}}) 302 ``` 303 304 # Other providers than Github 305 306 This library can be easily extended by providing a new source and release implementation for any git provider 307 Currently implemented are 308 - Github (default) 309 - Gitea 310 - Gitlab 311 312 # GitLab 313 314 Support for GitLab landed in version 1.0.0. 315 316 To be able to download assets from a private instance of GitLab, you have to publish your files to the [Generic Package Registry](https://docs.gitlab.com/ee/user/packages/package_registry/index.html). 317 318 If you're using goreleaser, you just need to add this option: 319 320 ```yaml 321 # .goreleaser.yml 322 gitlab_urls: 323 use_package_registry: true 324 325 ``` 326 327 See [goreleaser documentation](https://goreleaser.com/scm/gitlab/#generic-package-registry) for more information. 328 329 ## Example: 330 331 ```go 332 func update() { 333 source, err := selfupdate.NewGitLabSource(selfupdate.GitLabConfig{ 334 BaseURL: "https://private.instance.on.gitlab.com/", 335 }) 336 if err != nil { 337 log.Fatal(err) 338 } 339 updater, err := selfupdate.NewUpdater(selfupdate.Config{ 340 Source: source, 341 Validator: &selfupdate.ChecksumValidator{UniqueFilename: "checksums.txt"}, // checksum from goreleaser 342 }) 343 if err != nil { 344 log.Fatal(err) 345 } 346 release, found, err := updater.DetectLatest(context.Background(), selfupdate.NewRepositorySlug("owner", "cli-tool")) 347 if err != nil { 348 log.Fatal(err) 349 } 350 if !found { 351 log.Print("Release not found") 352 return 353 } 354 fmt.Printf("found release %s\n", release.Version()) 355 356 exe, err := os.Executable() 357 if err != nil { 358 return errors.New("could not locate executable path") 359 } 360 err = updater.UpdateTo(context.Background(), release, exe) 361 if err != nil { 362 log.Fatal(err) 363 } 364 } 365 ``` 366 367 # Copyright 368 369 This work is heavily based on: 370 371 372 - [go-github-selfupdate](https://github.com/rhysd/go-github-selfupdate): [Copyright (c) 2017 rhysd](https://github.com/rhysd/go-github-selfupdate/blob/master/LICENSE) 373 374 - [go-update](https://github.com/inconshreveable/go-update): [Copyright 2015 Alan Shreve](https://github.com/inconshreveable/go-update/blob/master/LICENSE)