updates.go 5.08 KB
Newer Older
1 2 3
package updates

import (
4
	"fmt"
5
	"os"
6
	"time"
7

Henry's avatar
Henry committed
8
	"github.com/jbenet/go-ipfs/config"
9 10
	u "github.com/jbenet/go-ipfs/util"

11 12 13
	semver "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
	update "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/inconshreveable/go-update"
	check "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/inconshreveable/go-update/check"
14 15 16
)

const (
17 18 19 20 21 22
	// Version is the current application's version literal
	Version = "0.1.1"

	updateEndpointURL = "https://api.equinox.io/1/Updates"
	updateAppID       = "CHANGEME"

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23
	// this is @jbenet's equinox.io public key.
24
	updatePubKey = `-----BEGIN RSA PUBLIC KEY-----
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
25 26 27 28 29 30 31
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxnwPPE4LNMjTfW/NRz1z
8uAPpwGYSzac+cwZbHbL5xFOxeX301GCdISaMm+Q8OEJqLyXfjYSuRwx00fDzWDD
ajBQOsxO08gTy1i/ow5YdEO+nYeVKO08fQFqVqdTz09BCgzt9iQJTEMeiq1kSWNo
al8usHD4SsNTxwDpSlok5UKWCHcr7D/TWX5A4B5A6ae9HSEcMB4Aum83k63Vzgm1
WTUvK0ed1zd0/KcHqIU36VZpVg4PeV4SWnOBnldQ98CWg/Mnqp3+lXMWYWTmXeX6
xj8JqOGpebzlxeISKE6fDBtrLxUbFTt3DNshl7S5CUGuc5H1MF1FTAyi+8u/nEZB
cQIDAQAB
32
-----END RSA PUBLIC KEY-----`
33 34
)

35 36
var log = u.Logger("updates")

37 38 39 40
var currentVersion *semver.Version

func init() {
	var err error
41
	currentVersion, err = parseVersion()
42
	if err != nil {
43
		log.Error("invalid version number in code (must be semver): %q\n", Version)
44 45
		os.Exit(1)
	}
Henry's avatar
Henry committed
46
	log.Info("go-ipfs Version: %s", currentVersion)
47 48
}

49 50 51
func parseVersion() (*semver.Version, error) {
	return semver.NewVersion(Version)
}
52

53
// CheckForUpdate checks the equinox.io api if there is an update available
54 55 56
func CheckForUpdate() (*check.Result, error) {
	param := check.Params{
		AppVersion: Version,
57
		AppId:      updateAppID,
58
		Channel:    "stable",
59
	}
60

61 62 63 64 65 66 67 68
	up, err := update.New().VerifySignatureWithPEM([]byte(updatePubKey))
	if err != nil {
		return nil, fmt.Errorf("Failed to parse public key: %v", err)
	}

	return param.CheckForUpdate(updateEndpointURL, up)
}

Henry's avatar
Henry committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
// Apply cheks if the running process is able to update itself
// and than updates to the passed release
func Apply(rel *check.Result) error {
	if err := update.New().CanUpdate(); err != nil {
		return err
	}

	if err, errRecover := rel.Update(); err != nil {
		err = fmt.Errorf("Update failed: %v\n", err)
		if errRecover != nil {
			err = fmt.Errorf("%s\nRecovery failed! Cause: %v\nYou may need to recover manually", err, errRecover)
		}
		return err
	}

	return nil
85
}
Henry's avatar
Henry committed
86 87 88

// ShouldAutoUpdate decides wether a new version should be applied
// checks against config setting and new version string. returns false in case of error
89
func ShouldAutoUpdate(setting config.AutoUpdateSetting, newVer string) bool {
Henry's avatar
Henry committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	if setting == config.UpdateNever {
		return false
	}

	nv, err := semver.NewVersion(newVer)
	if err != nil {
		log.Error("could not parse version string: %s", err)
		return false
	}

	n := nv.Slice()
	c := currentVersion.Slice()

	switch setting {

	case config.UpdatePatch:
		if n[0] < c[0] {
			return false
		}

		if n[1] < c[1] {
			return false
		}

		return n[2] > c[2]

	case config.UpdateMinor:
		if n[0] != c[0] {
			return false
		}

		return n[1] > c[1] || (n[1] == c[1] && n[2] > c[2])

	case config.UpdateMajor:
		for i := 0; i < 3; i++ {
			if n[i] < c[i] {
				return false
			}
		}
		return true
	}

	return false
}
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

func CliCheckForUpdates(cfg *config.Config, confFile string) error {

	// if config says not to, don't check for updates
	if !cfg.Version.ShouldCheckForUpdate() {
		log.Info("update checking disabled.")
		return nil
	}

	log.Info("checking for update")
	u, err := CheckForUpdate()
	// if there is no update available, record it, and exit.
	if err == check.NoUpdateAvailable {
		log.Notice("No update available, checked on %s", time.Now())
		config.RecordUpdateCheck(cfg, confFile) // only record if we checked successfully.
		return nil
	}

	// if another, unexpected error occurred, note it.
	if err != nil {
		if cfg.Version.Check == config.CheckError {
			log.Error("Error while checking for update: %v\n", err)
			return nil
		}
		// when "warn" version.check mode we just show a warning message
		log.Warning(err.Error())
		return nil
	}

	// there is an update available

	// if we autoupdate
	if cfg.Version.AutoUpdate != config.UpdateNever {
		// and we should auto update
		if ShouldAutoUpdate(cfg.Version.AutoUpdate, u.Version) {
			log.Notice("Applying update %s", u.Version)

			if err = Apply(u); err != nil {
				log.Error(err.Error())
				return nil
			}

			// BUG(cryptix): no good way to restart yet. - tracking https://github.com/inconshreveable/go-update/issues/5
Henry's avatar
Henry committed
177
			fmt.Printf("update %v applied. please restart.\n", u.Version)
178 179 180 181 182 183 184 185 186 187
			os.Exit(0)
		}
	}

	// autoupdate did not exit, so regular notices.
	switch cfg.Version.Check {
	case config.CheckError:
		return fmt.Errorf(errShouldUpdate, Version, u.Version)
	case config.CheckWarn:
		// print the warning
Henry's avatar
Henry committed
188
		fmt.Printf("New version available: %s\n", u.Version)
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
	default: // ignore
	}
	return nil
}

var errShouldUpdate = `
Your go-ipfs version is: %s
There is a new version available: %s
Since this is alpha software, it is strongly recommended you update.

To update, run:

    ipfs update apply

To disable this notice, run:

    ipfs config Version.Check warn

`