Преглед на файлове

Built in upgrade functionality

Jakob Borg преди 11 години
родител
ревизия
33d75a264d
променени са 3 файла, в които са добавени 163 реда и са изтрити 0 реда
  1. 10 0
      cmd/syncthing/main.go
  2. 146 0
      cmd/syncthing/upgrade_unix.go
  3. 7 0
      cmd/syncthing/upgrade_windows.go

+ 10 - 0
cmd/syncthing/main.go

@@ -83,9 +83,11 @@ const (
 func main() {
 	var reset bool
 	var showVersion bool
+	var doUpgrade bool
 	flag.StringVar(&confDir, "home", getDefaultConfDir(), "Set configuration directory")
 	flag.BoolVar(&reset, "reset", false, "Prepare to resync from cluster")
 	flag.BoolVar(&showVersion, "version", false, "Show version")
+	flag.BoolVar(&doUpgrade, "upgrade", false, "Perform upgrade")
 	flag.Usage = usageFor(flag.CommandLine, usage, extraUsage)
 	flag.Parse()
 
@@ -99,6 +101,14 @@ func main() {
 		return
 	}
 
+	if doUpgrade {
+		err := upgrade()
+		if err != nil {
+			fatalln(err)
+		}
+		return
+	}
+
 	if len(os.Getenv("GOGC")) == 0 {
 		debug.SetGCPercent(25)
 	}

+ 146 - 0
cmd/syncthing/upgrade_unix.go

@@ -0,0 +1,146 @@
+// +build !windows
+
+package main
+
+import (
+	"archive/tar"
+	"compress/gzip"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"path"
+	"path/filepath"
+	"runtime"
+	"strings"
+
+	"bitbucket.org/kardianos/osext"
+)
+
+type githubRelease struct {
+	Tag      string        `json:"tag_name"`
+	Prelease bool          `json:"prerelease"`
+	Assets   []githubAsset `json:"assets"`
+}
+
+type githubAsset struct {
+	URL  string `json:"url"`
+	Name string `json:"name"`
+}
+
+func upgrade() error {
+	path, err := osext.Executable()
+	if err != nil {
+		return err
+	}
+
+	resp, err := http.Get("https://api.github.com/repos/calmh/syncthing/releases?per_page=1")
+	if err != nil {
+		return err
+	}
+
+	var rels []githubRelease
+	json.NewDecoder(resp.Body).Decode(&rels)
+	resp.Body.Close()
+
+	if len(rels) != 1 {
+		return fmt.Errorf("Unexpected number of releases: %d", len(rels))
+	}
+	rel := rels[0]
+
+	if rel.Tag > Version {
+		infof("Attempting upgrade to %s...", rel.Tag)
+	} else if rel.Tag == Version {
+		okf("Already running the latest version, %s. Not upgrading.", Version)
+		return nil
+	} else {
+		okf("Current version %s is newer than latest release %s. Not upgrading.", Version, rel.Tag)
+		return nil
+	}
+
+	expectedRelease := fmt.Sprintf("syncthing-%s-%s-%s.", runtime.GOOS, runtime.GOARCH, rel.Tag)
+	for _, asset := range rel.Assets {
+		if strings.HasPrefix(asset.Name, expectedRelease) {
+			if strings.HasSuffix(asset.Name, ".tar.gz") {
+				infof("Downloading %s...", asset.Name)
+				fname, err := readTarGZ(asset.URL)
+				if err != nil {
+					return err
+				}
+
+				old := path + "." + Version
+				err = os.Rename(path, old)
+				if err != nil {
+					return err
+				}
+				err = os.Rename(fname, path)
+				if err != nil {
+					return err
+				}
+
+				okf("Upgraded %q to %s.", path, rel.Tag)
+				okf("Previous version saved in %q.", old)
+
+				return nil
+			}
+		}
+	}
+
+	return nil
+}
+
+func readTarGZ(url string) (string, error) {
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return "", err
+	}
+
+	req.Header.Add("Accept", "application/octet-stream")
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return "", err
+	}
+	defer resp.Body.Close()
+
+	gr, err := gzip.NewReader(resp.Body)
+	if err != nil {
+		return "", err
+	}
+
+	tr := tar.NewReader(gr)
+	if err != nil {
+		return "", err
+	}
+
+	// Iterate through the files in the archive.
+	for {
+		hdr, err := tr.Next()
+		if err == io.EOF {
+			// end of tar archive
+			break
+		}
+		if err != nil {
+			return "", err
+		}
+
+		if path.Base(hdr.Name) == "syncthing" {
+			fname := filepath.Join(os.TempDir(), "syncthing.new")
+			of, err := os.Create(fname)
+			if err != nil {
+				return "", err
+			}
+			io.Copy(of, tr)
+			err = of.Close()
+			if err != nil {
+				os.Remove(fname)
+				return "", err
+			}
+
+			os.Chmod(fname, os.FileMode(hdr.Mode))
+			return fname, nil
+		}
+	}
+
+	return "", fmt.Errorf("No upgrade found")
+}

+ 7 - 0
cmd/syncthing/upgrade_windows.go

@@ -0,0 +1,7 @@
+// +build windows
+
+package main
+
+func upgrade() {
+	fatalln("Upgrade currently unsupported on Windows")
+}