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)