Transforming multi-git to a Self-Updateable Program

In this lesson, we'll use the `go-github-update` third party library to make multi-git self-updateable by checking the latest release and fetch it if it is newer than the running program.

Using go-github-selfupdate

The go-github-selfupdate library has many things going for it:

  • It automatically detects the latest version of the released binary on GitHub
  • It retrieves the proper binary for the OS and arch where the binary is running
  • It updates the binary with rollback support on failure
  • It is tested on Linux, macOS and Windows (using Travis CI and AppVeyor)
  • It supports many archive and compression formats (zip, tar, gzip, xzip)
  • It supports private repositories
  • It supports GitHub Enterprise
  • It supports hash and signature validation

To detect the latest version, it requires a semantic versioning scheme. Let’s see how we can implement an auto-update functionality for multi-git.

To simplify matters let’s remove the embedded git tag and build a timestamp. We will replace them with an explicit version variable:

package main

import (
	"fmt"
	"log"

	"github.com/blang/semver"
	"github.com/rhysd/go-github-selfupdate/selfupdate"

	"github.com/the-gigi/multi-git/cmd"
)

const (
	version = "v0.8.18"
)

In the main() function, we will parse the version using the semver package and then call selfupdate.UpdateSelf(the-gigi/multi-git):

func main() {
	fmt.Println("version: ", version)
	v := semver.MustParse(version[1:])
	latest, err := selfupdate.UpdateSelf(v, "the-gigi/multi-git")
	if err != nil {
		log.Fatalf("Binary update failed: %v", err)
		return
	} else {
		fmt.Println("Current version is:", latest.Version)
	}

	cmd.Execute()
}

The selfupdate package does the rest:

  • Compares the latest release tag against current version
  • Updates if necessary
  • Displays the error message or the new version

Auto-updating with new releases

Let’s see it in action. Before making changes we have:

$ multi-git -h | grep version
version:  v0.8.18
Current version is: 0.8.18

Now, let’s update the version to 0.8.19

const (
	version = "v0.8.19"
)

Then, add commit, push and tag:

(🐙)/multi-git/
$ git add -A .

(🐙)/multi-git/
$ git commit -m "bump version to v0.8.19"
[master e78e9be] bump version to v0.8.19
 1 file changed, 1 insertion(+), 1 deletion(-)

(🐙)/multi-git/
$ git push
Enumerating objects: 5, done.
Delta compression using up to 16 threads
Total 3 (delta 2), reused 0 (delta 0)
To github.com:the-gigi/multi-git.git
 	refs/heads/master:refs/heads/master	172b801..e78e9be
Done

(🐙)/multi-git/
$ git push --tags
Total 0 (delta 0), reused 0 (delta 0)
To github.com:the-gigi/multi-git.git
 * [new tag]         v0.8.19 -> v0.8.19

Now, we need to wait for the workflow to finish and once release v0.8.19 is ready on GitHub, we can run multi-git again, using the same executable:

(🐙)/multi-git/
$ multi-git -h | grep version
version:  v0.8.18
Current version is: 0.8.19

Multi-git was automatically updated to 0.8.19. Let’s run it again to make sure:

(🐙)/multi-git/
$ multi-git -h | grep version
version:  v0.8.19
Current version is: 0.8.19

Get hands-on with 1200+ tech skills courses.