{"id":16408643,"url":"https://github.com/creativeprojects/go-selfupdate","last_synced_at":"2025-04-05T08:03:45.083Z","repository":{"id":38304199,"uuid":"311040306","full_name":"creativeprojects/go-selfupdate","owner":"creativeprojects","description":"self update your applications in go","archived":false,"fork":false,"pushed_at":"2025-03-19T14:57:28.000Z","size":338,"stargazers_count":80,"open_issues_count":1,"forks_count":16,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-29T07:03:39.959Z","etag":null,"topics":["arm-architecture","autoupdate","autoupdater","autoupgrade","gitea","github","gitlab","go","goreleaser","hash","self-updater","selfupdate","signature-validation","update","updater","upgrade"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/creativeprojects.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-08T10:48:01.000Z","updated_at":"2025-03-21T15:29:23.000Z","dependencies_parsed_at":"2023-02-09T22:05:40.310Z","dependency_job_id":"c9b81345-c019-4cf3-a911-ba301b9e47c3","html_url":"https://github.com/creativeprojects/go-selfupdate","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creativeprojects%2Fgo-selfupdate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creativeprojects%2Fgo-selfupdate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creativeprojects%2Fgo-selfupdate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creativeprojects%2Fgo-selfupdate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/creativeprojects","download_url":"https://codeload.github.com/creativeprojects/go-selfupdate/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247305930,"owners_count":20917207,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arm-architecture","autoupdate","autoupdater","autoupgrade","gitea","github","gitlab","go","goreleaser","hash","self-updater","selfupdate","signature-validation","update","updater","upgrade"],"created_at":"2024-10-11T06:17:24.763Z","updated_at":"2025-04-05T08:03:45.055Z","avatar_url":"https://github.com/creativeprojects.png","language":"Go","readme":"Self-Update library for Github, Gitea and Gitlab hosted applications in Go\n==============================================================\n\n[![Godoc reference](https://godoc.org/github.com/creativeprojects/go-selfupdate?status.svg)](http://godoc.org/github.com/creativeprojects/go-selfupdate)\n[![Build](https://github.com/creativeprojects/go-selfupdate/workflows/Build/badge.svg)](https://github.com/creativeprojects/go-selfupdate/actions)\n[![codecov](https://codecov.io/gh/creativeprojects/go-selfupdate/branch/main/graph/badge.svg?token=3FejM0fkw2)](https://codecov.io/gh/creativeprojects/go-selfupdate)\n[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=creativeprojects_go-selfupdate\u0026metric=bugs)](https://sonarcloud.io/summary/new_code?id=creativeprojects_go-selfupdate)\n[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=creativeprojects_go-selfupdate\u0026metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=creativeprojects_go-selfupdate)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=creativeprojects_go-selfupdate\u0026metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=creativeprojects_go-selfupdate)\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=creativeprojects_go-selfupdate\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=creativeprojects_go-selfupdate)\n[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=creativeprojects_go-selfupdate\u0026metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=creativeprojects_go-selfupdate)\n\n\u003c!--ts--\u003e\n* [Self\\-Update library for Github, Gitea and Gitlab hosted applications in Go](#self-update-library-for-github-gitea-and-gitlab-hosted-applications-in-go)\n* [Introduction](#introduction)\n* [Example](#example)\n* [Upgrade from v0\\+ to v1](#upgrade-from-v0-to-v1)\n  * [Repository](#repository)\n    * [ParseSlug](#parseslug)\n    * [NewRepositorySlug](#newrepositoryslug)\n    * [NewRepositoryID (GitLab only)](#newrepositoryid-gitlab-only)\n    * [Context](#context)\n  * [Package functions](#package-functions)\n  * [Methods on Source interface](#methods-on-source-interface)\n  * [Methods on Updater struct](#methods-on-updater-struct)\n* [Naming Rules of Released Binaries](#naming-rules-of-released-binaries)\n* [Naming Rules of Versions (=Git Tags)](#naming-rules-of-versions-git-tags)\n* [Structure of Releases](#structure-of-releases)\n* [Special case for ARM architecture](#special-case-for-arm-architecture)\n* [Hash or Signature Validation](#hash-or-signature-validation)\n  * [SHA256](#sha256)\n  * [ECDSA](#ecdsa)\n  * [Using a single checksum file for all your assets](#using-a-single-checksum-file-for-all-your-assets)\n* [macOS universal binaries](#macos-universal-binaries)\n* [Other providers than Github](#other-providers-than-github)\n* [GitLab](#gitlab)\n  * [Example:](#example-1)\n* [Http Based Repository](#http-based-repository)\n  * [Example:](#example-2)\n* [Copyright](#copyright)\n\n\u003c!--te--\u003e\n\n# Introduction\n\n`go-selfupdate` detects the information of the latest release via a source provider and\nchecks the current version. If a newer version than itself is detected, it downloads the released binary from\nthe source provider and replaces itself.\n\n- Automatically detect the latest version of released binary on the source provider\n- Retrieve the proper binary for the OS and arch where the binary is running\n- Update the binary with rollback support on failure\n- Tested on Linux, macOS and Windows\n- Support for different versions of ARM architecture\n- Support macOS universal binaries\n- Many archive and compression formats are supported (zip, tar, gzip, xz, bzip2)\n- Support private repositories\n- Support hash, signature validation\n\nThree source providers are available:\n- GitHub\n- Gitea\n- Gitlab\n\nThis library started as a fork of https://github.com/rhysd/go-github-selfupdate. A few things have changed from the original implementation:\n- don't expose an external `semver.Version` type, but provide the same functionality through the API: `LessThan`, `Equal` and `GreaterThan`\n- use an interface to send logs (compatible with standard log.Logger)\n- able to detect different ARM CPU architectures (the original library wasn't working on my different versions of raspberry pi)\n- support for assets compressed with bzip2 (.bz2)\n- can use a single file containing the sha256 checksums for all the files (one per line)\n- separate the provider and the updater, so we can add more providers (Github, Gitea, Gitlab, etc.)\n- return well defined wrapped errors that can be checked with `errors.Is(err error, target error)`\n\n# Example\n\nHere's an example how to use the library for an application to update itself\n\n```go\nfunc update(version string) error {\n\tlatest, found, err := selfupdate.DetectLatest(context.Background(), selfupdate.ParseSlug(\"creativeprojects/resticprofile\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error occurred while detecting version: %w\", err)\n\t}\n\tif !found {\n\t\treturn fmt.Errorf(\"latest version for %s/%s could not be found from github repository\", runtime.GOOS, runtime.GOARCH)\n\t}\n\n\tif latest.LessOrEqual(version) {\n\t\tlog.Printf(\"Current version (%s) is the latest\", version)\n\t\treturn nil\n\t}\n\n\texe, err := selfupdate.ExecutablePath()\n\tif err != nil {\n\t\treturn errors.New(\"could not locate executable path\")\n\t}\n\tif err := selfupdate.UpdateTo(context.Background(), latest.AssetURL, latest.AssetName, exe); err != nil {\n\t\treturn fmt.Errorf(\"error occurred while updating binary: %w\", err)\n\t}\n\tlog.Printf(\"Successfully updated to version %s\", latest.Version())\n\treturn nil\n}\n```\n\n# Upgrade from v0+ to v1\n\nVersion v1+ has a **stable** API. It is slightly different from the API of versions 0+.\n\n## Repository\n\nSome functions needed a couple `owner`/`repo` and some other a single string called `slug`. These have been replaced by a `Repository`.\n\nTwo constructors are available:\n\n### ParseSlug\n\nParses a *slug* string like `owner/repository_name`\n\n```go\nfunc ParseSlug(slug string) RepositorySlug\n```\n\n### NewRepositorySlug\n\nCreates a repository from both owner and repo strings\n\n```go\nfunc NewRepositorySlug(owner, repo string) RepositorySlug\n```\n\n### NewRepositoryID (GitLab only)\n\nGitLab can also refer to a repository via its internal ID. This constructor can be used with a numeric repository ID.\n\n```go\nfunc NewRepositoryID(id int) RepositoryID\n```\n\n### Context\n\nAll methods are now accepting a `context` as their first parameter. You can use it to cancel a long running operation.\n\n## Package functions\n\n| v0 | v1 |\n|----|----|\n| UpdateTo(assetURL, assetFileName, cmdPath string) error | UpdateTo(ctx context.Context, assetURL, assetFileName, cmdPath string) error |\n| DetectLatest(slug string) (*Release, bool, error) | DetectLatest(ctx context.Context, repository Repository) (*Release, bool, error) |\n| DetectVersion(slug string, version string) (*Release, bool, error) | DetectVersion(ctx context.Context, repository Repository, version string) (*Release, bool, error) |\n| UpdateCommand(cmdPath string, current string, slug string) (*Release, error) | UpdateCommand(ctx context.Context, cmdPath string, current string, repository Repository) (*Release, error) |\n| UpdateSelf(current string, slug string) (*Release, error) | UpdateSelf(ctx context.Context, current string, repository Repository) (*Release, error) |\n\n## Methods on Source interface\n\n| v0 | v1 |\n|----|----|\n| ListReleases(owner, repo string) ([]SourceRelease, error) | ListReleases(ctx context.Context, repository Repository) ([]SourceRelease, error) |\n| DownloadReleaseAsset(owner, repo string, releaseID, id int64) (io.ReadCloser, error) | DownloadReleaseAsset(ctx context.Context, rel *Release, assetID int64) (io.ReadCloser, error) |\n\n## Methods on Updater struct\n\n| v0 | v1 |\n|----|----|\n| DetectLatest(slug string) (release *Release, found bool, err error) | DetectLatest(ctx context.Context, repository Repository) (release *Release, found bool, err error) |\n| 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) |\n| UpdateCommand(cmdPath string, current string, slug string) (*Release, error) | UpdateCommand(ctx context.Context, cmdPath string, current string, repository Repository) (*Release, error) |\n| UpdateSelf(current string, slug string) (*Release, error) | UpdateSelf(ctx context.Context, current string, repository Repository) (*Release, error) |\n| UpdateTo(rel *Release, cmdPath string) error | UpdateTo(ctx context.Context, rel *Release, cmdPath string) error |\n\n\n# Naming Rules of Released Binaries\n\ngo-selfupdate assumes that released binaries are put for each combination of platforms and architectures.\nBinaries for each platform can be easily built using tools like [goreleaser][]\n\nYou need to put the binaries with the following format.\n\n```\n{cmd}_{goos}_{goarch}{.ext}\n```\n\n`{cmd}` is a name of command.\n`{goos}` and `{goarch}` are the platform and the arch type of the binary.\n`{.ext}` is a file extension. go-selfupdate supports `.zip`, `.gzip`, `.bz2`, `.tar.gz` and `.tar.xz`.\nYou can also use blank and it means binary is not compressed.\n\nIf you compress binary, uncompressed directory or file must contain the executable named `{cmd}`.\n\nAnd you can also use `-` for separator instead of `_` if you like.\n\nFor example, if your command name is `foo-bar`, one of followings is expected to be put in release\npage on GitHub as binary for platform `linux` and arch `amd64`.\n\n- `foo-bar_linux_amd64` (executable)\n- `foo-bar_linux_amd64.zip` (zip file)\n- `foo-bar_linux_amd64.tar.gz` (tar file)\n- `foo-bar_linux_amd64.xz` (xzip file)\n- `foo-bar-linux-amd64.tar.gz` (`-` is also ok for separator)\n\nIf you compress and/or archive your release asset, it must contain an executable named one of followings:\n\n- `foo-bar` (only command name)\n- `foo-bar_linux_amd64` (full name)\n- `foo-bar-linux-amd64` (`-` is also ok for separator)\n\nTo archive the executable directly on Windows, `.exe` can be added before file extension like\n`foo-bar_windows_amd64.exe.zip`.\n\n[goreleaser]: https://github.com/goreleaser/goreleaser/\n\n\n# Naming Rules of Versions (=Git Tags)\n\ngo-selfupdate searches binaries' versions via Git tag names (not a release title).\nWhen your tool's version is `1.2.3`, you should use the version number for tag of the Git\nrepository (i.e. `1.2.3` or `v1.2.3`).\n\nThis library assumes you adopt [semantic versioning][]. It is necessary for comparing versions\nsystematically.\n\nPrefix before version number `\\d+\\.\\d+\\.\\d+` is automatically omitted. For example, `ver1.2.3` or\n`release-1.2.3` are also ok.\n\nTags which don't contain a version number are ignored (i.e. `nightly`). And releases marked as `pre-release`\nare also ignored.\n\n[semantic versioning]: https://semver.org/\n\n\n# Structure of Releases\n\nIn summary, structure of releases on GitHub looks like:\n\n- `v1.2.0`\n  - `foo-bar-linux-amd64.tar.gz`\n  - `foo-bar-linux-386.tar.gz`\n  - `foo-bar-darwin-amd64.tar.gz`\n  - `foo-bar-windows-amd64.zip`\n  - ... (Other binaries for v1.2.0)\n- `v1.1.3`\n  - `foo-bar-linux-amd64.tar.gz`\n  - `foo-bar-linux-386.tar.gz`\n  - `foo-bar-darwin-amd64.tar.gz`\n  - `foo-bar-windows-amd64.zip`\n  - ... (Other binaries for v1.1.3)\n- ... (older versions)\n\n# Special case for ARM architecture\n\nIf you're using [goreleaser](https://github.com/goreleaser/goreleaser/) targeting ARM CPUs, it will use the version of the ARM architecture as a name:\n- `armv5`\n- `armv6`\n- `armv7`\n\ngo-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.\n\nAs 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.\n\nSo if you're running a `armv6` binary, it will try these targets in order:\n- `armv6`\n- `armv5`\n- `arm`\n\nMore information on targeting ARM cpu can be found here: [GoArm](https://github.com/golang/go/wiki/GoArm)\n\n# Hash or Signature Validation\n\ngo-selfupdate supports hash or signature validation of the downloaded files. It comes\nwith support for sha256 hashes or ECDSA signatures. If you need something different,\nyou can implement the `Validator` interface with your own validation mechanism:\n\n```go\n// Validator represents an interface which enables additional validation of releases.\ntype Validator interface {\n\t// Validate validates release bytes against an additional asset bytes.\n\t// See SHAValidator or ECDSAValidator for more information.\n\tValidate(filename string, release, asset []byte) error\n\t// GetValidationAssetName returns the additional asset name containing the validation checksum.\n\t// The asset containing the checksum can be based on the release asset name\n\t// Please note if the validation file cannot be found, the DetectLatest and DetectVersion methods\n\t// will fail with a wrapped ErrValidationAssetNotFound error\n\tGetValidationAssetName(releaseFilename string) string\n}\n```\n\n## SHA256\n\nTo verify the integrity by SHA256, generate a hash sum and save it within a file which has the\nsame naming as original file with the suffix `.sha256`.\nFor e.g. use sha256sum, the file `selfupdate/testdata/foo.zip.sha256` is generated with:\n```shell\nsha256sum foo.zip \u003e foo.zip.sha256\n```\n\n## ECDSA\nTo verify the signature by ECDSA generate a signature and save it within a file which has the\nsame naming as original file with the suffix `.sig`.\nFor e.g. use openssl, the file `selfupdate/testdata/foo.zip.sig` is generated with:\n```shell\nopenssl dgst -sha256 -sign Test.pem -out foo.zip.sig foo.zip\n```\n\ngo-selfupdate makes use of go internal crypto package. Therefore the private key\nhas to be compatible with FIPS 186-3.\n\n## Using a single checksum file for all your assets\n\nTools like [goreleaser][] produce a single checksum file for all your assets. A Validator is provided out of the box for this case:\n\n```go\nupdater, _ := NewUpdater(Config{Validator: \u0026ChecksumValidator{UniqueFilename: \"checksums.txt\"}})\n```\n\n# macOS universal binaries\n\nYou can ask the updater to choose a macOS universal binary as a fallback if the native architecture wasn't found.\n\nYou need to provide the architecture name for the universal binary in the `Config` struct:\n\n```go\nupdater, _ := NewUpdater(Config{UniversalArch: \"all\"})\n```\n\nDefault is empty, which means no fallback.\n\n# Other providers than Github\n\nThis library can be easily extended by providing a new source and release implementation for any git provider\nCurrently implemented are \n- Github (default)\n- Gitea\n- Gitlab\n\n# GitLab\n\nSupport for GitLab landed in version 1.0.0.\n\nTo 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).\n\nIf you're using goreleaser, you just need to add this option:\n\n```yaml\n# .goreleaser.yml\ngitlab_urls:\n  use_package_registry: true\n\n```\n\nSee [goreleaser documentation](https://goreleaser.com/scm/gitlab/#generic-package-registry) for more information.\n\n## Example:\n\n```go\nfunc update() {\n\tsource, err := selfupdate.NewGitLabSource(selfupdate.GitLabConfig{\n\t\tBaseURL: \"https://private.instance.on.gitlab.com/\",\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tupdater, err := selfupdate.NewUpdater(selfupdate.Config{\n\t\tSource:    source,\n\t\tValidator: \u0026selfupdate.ChecksumValidator{UniqueFilename: \"checksums.txt\"}, // checksum from goreleaser\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\trelease, found, err := updater.DetectLatest(context.Background(), selfupdate.NewRepositorySlug(\"owner\", \"cli-tool\"))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif !found {\n\t\tlog.Print(\"Release not found\")\n\t\treturn\n\t}\n\tfmt.Printf(\"found release %s\\n\", release.Version())\n\n\texe, err := selfupdate.ExecutablePath()\n\tif err != nil {\n\t\treturn errors.New(\"could not locate executable path\")\n\t}\n\terr = updater.UpdateTo(context.Background(), release, exe)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n# Http Based Repository\n\nSupport for http based repositories landed in version 1.4.0.\n\nThe HttpSource is designed to work with repositories built using [goreleaser-http-repo-builder](https://github.com/GRMrGecko/goreleaser-http-repo-builder?tab=readme-ov-file). This provides a simple way to add self-update support to software that is not open source, allowing you to host your own updates. It requires that you still use the owner/project url style, and you can set custom headers to be used with requests to authenticate.\n\n## Example:\n\nIf your repository is at example.com/repo/project, then you'd use the following example.\n\n```go\nfunc update() {\n\tsource, err := selfupdate.NewHttpSource(selfupdate.HttpConfig{\n\t\tBaseURL: \"https://example.com/\",\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tupdater, err := selfupdate.NewUpdater(selfupdate.Config{\n\t\tSource:    source,\n\t\tValidator: \u0026selfupdate.ChecksumValidator{UniqueFilename: \"checksums.txt\"}, // checksum from goreleaser\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\trelease, found, err := updater.DetectLatest(context.Background(), selfupdate.NewRepositorySlug(\"repo\", \"project\"))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif !found {\n\t\tlog.Print(\"Release not found\")\n\t\treturn\n\t}\n\tfmt.Printf(\"found release %s\\n\", release.Version())\n\n\texe, err := selfupdate.ExecutablePath()\n\tif err != nil {\n\t\treturn errors.New(\"could not locate executable path\")\n\t}\n\terr = updater.UpdateTo(context.Background(), release, exe)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n# Copyright\n\nThis work is heavily based on:\n\n\n- [go-github-selfupdate](https://github.com/rhysd/go-github-selfupdate): [Copyright (c) 2017 rhysd](https://github.com/rhysd/go-github-selfupdate/blob/master/LICENSE)\n\n- [go-update](https://github.com/inconshreveable/go-update): [Copyright 2015 Alan Shreve](https://github.com/inconshreveable/go-update/blob/master/LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcreativeprojects%2Fgo-selfupdate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcreativeprojects%2Fgo-selfupdate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcreativeprojects%2Fgo-selfupdate/lists"}