A simple way to self update Go applications from hosted prebuilt binaries.
- Supports downloading and extracting binaries from archives (.tar.gz or .zip)
- Supports downloading binaries
- Supports downloading updates from files hosted on GitHub releases.
Updater expects the following files to be hosted together under the same base url.
- A manifest json file.
- The prebuilt binaries or archives (.tar.gz or .zip) containing the prebuilt binaries.
The manifest file describes the following
- App version
- os and architecture specific file names for archives/binaries.
- Binary name
Updater downloads the manifest to check the available binaries/archives and installs the one appropriate for the current os and architecture. It then replaces the currently running binary with the newly downloaded binary.
Here is a simple example where prebuilt binaries are hosted on GitHub releases within archives.
Files hosted on GitHub releases:
- scf_Darwin_x86_64.tar.gz
- scf_Linux_x86_64.tar.gz
- scf_Windows_x86_64.zip
- updater.config.json
updater.config.json
{
"version": "1.0.0",
"archive": "scf_{{.Os}}_{{.Arch}}{{.ArchiveExt}}",
"binary": "scf{{.Ext}}",
"os": {
"linux": "Linux",
"darwin": "Darwin",
"windows": "Windows"
},
"arch": {
"Windows": {
"amd64": "x86_64"
},
"Linux": {
"amd64": "x86_64"
},
"Darwin": {
"amd64": "x86_64"
}
}
}
View the full manifest reference for more details.
Example updater code
package main
import (
"fmt"
"github.com/dworthen/updater"
)
func main() {
pkgUpdater := updater.New(&updater.UpdaterConfig{
CurrentVersion: "0.0.1",
BaseUrl: "https://github.com/dworthen/scf/releases/latest/download",
UpdaterConfig: "updater.config.json"
})
// Check for updates.
isUpdate, newVersion, err := pkgUpdater.CheckForAvailableUpdate()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if isUpdate {
fmt.Printf("New update available. Updating to %v", newVersion)
// Perform update
err = pkgUpdater.Update()
if err != nil {
var notSupportedError *updater.NotSupportedError
if errors.As(err, ¬SupportedError) {
fmt.Printf(
"Self updating is not supported for %s. Please reinstall.",
notSupportedError.Platform
)
} else {
fmt.Println(err)
}
os.Exit(1)
}
fmt.Println("Updated!")
}
}
You can view the hosted files for this sample here along with the usage of updater here.
CurrentVersion
: The current version of the application. This is used inCheckForAvailableUpdate
. The method checks that theCurrentVersion
and hosted manifestversion
differ to determine that there is an update available. Updater only checks that these values differ and does not try to parse them as semantic versions or determine if the hosted version is greater than the current version. The idea is that the location provided byBaseUrl
is where the latest, ready-to-go, binaries are stored.UpdaterConfig
: Name of the updater manifest file hosted at theBaseUrl
.BaesUrl
: Url where all the files are hosted. Updater will first download theUpdaterConfig
file from this location and then use the values within the manifest to download the appropriate archive/binary from the sameBaseUrl
location. Updater expects the manifest to be hosted along side the binaries/archives.
version
(string) [Required]: The version ofarchive
(text/template string) [Optional]: Describes the archive names where the binaries are stored. If not provided, updater will download the direct binaries as specified by thebinary
key.binary
(text/template string) [Required]: The name of the binary. If thearchive
key is provided, updater will extract the binary from the archive. This should be the name of the binary file only, not the path. For example, if the archive contains a directory that then contains the binary, only provide the binary name, updater will search through all directories for the binary. If multiple directories exist within the archive that contain the binary, updater will use the first found binary that matches the name. If thearchive
key is not provided then updater will try to download the binary directly from theBaseUrl
.os
(map[string]string) [Required]: A mapping of os names as returned byruntime.GOOS
to the value that is used by the hosted archive/binary file names. This value is required even if using the default values, e.g.,windows
=windows
.arch
(map[string]map[string]string) [Required]: A mapping of architectures as returned byruntime.GOARCH
to what is used by the hosted archive/binary file names. This map is scoped by os so it is possible to map the termamd64
tox86_64
for linux builds but leave as is for windows. This value is required even if using the default values, e.g.,linux.amd64
=amd64
.
Note
os
and arch
mappings are required even if using the default values. Updater uses these maps to determine which platforms are supported for updating, that way avoiding random requests to the BaseUrl
. For example, if a user is running the application on freebsd
but prebuilt binaries are only available for darwin
, linux
and windows
then when updater is unable to find the freebsd
key in the os
map it will assume that self-updating is not supported on that platform and report that in ERROR and avoid making a request to the BaseUrl
for the freebsd
based binary.
Run go tool dist list
to view the full list of possible os
/arch
combinations.
The archive
and binary
template strings have access to the following variables:
OS
: The operating system as defined theos
mapping.Arch
: The architecture as defined by thearch
mapping. In the above example,Arch
is set tox86_64
instead ofamd64
on all systems due to thearch
mapping.ArchiveExt
:.zip
on Windows and.tar.gz
on other platforms.Ext
: The binary extension..exe
on Windows and the empty string on other platforms.