update.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package main
  2. import (
  3. "bufio"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "os"
  12. "path/filepath"
  13. "runtime"
  14. "strconv"
  15. "strings"
  16. "github.com/codeskyblue/goreq"
  17. "github.com/getlantern/go-update"
  18. "github.com/mholt/archiver"
  19. "github.com/mitchellh/ioprogress"
  20. )
  21. func formatString(format string, params map[string]string) string {
  22. for k, v := range params {
  23. format = strings.Replace(format, "{"+k+"}", v, -1)
  24. }
  25. return format
  26. }
  27. func makeTempDir() string {
  28. if runtime.GOOS == "linux" && runtime.GOARCH == "arm" {
  29. target := "/data/local/tmp/atx-update.tmp"
  30. os.MkdirAll(target, 0755)
  31. return target
  32. }
  33. os.MkdirAll("atx-update.tmp", 0755)
  34. return "atx-update.tmp"
  35. }
  36. func getLatestVersion() (version string, err error) {
  37. res, err := goreq.Request{
  38. Uri: fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo),
  39. }.WithHeader("Authorization", "token e83785ff4e37c67098efcea923b668f4135d1dda").Do() // this GITHUB_TOKEN is only for get lastest version
  40. if err != nil {
  41. return
  42. }
  43. defer res.Body.Close()
  44. if res.StatusCode != http.StatusOK {
  45. return "", fmt.Errorf("http status code is not 200, got %d", res.StatusCode)
  46. }
  47. var t = struct {
  48. TagName string `json:"tag_name"`
  49. }{}
  50. if err = json.NewDecoder(res.Body).Decode(&t); err != nil {
  51. return
  52. }
  53. if t.TagName == "" {
  54. return "", errors.New("TagName empty")
  55. }
  56. return t.TagName, nil
  57. }
  58. func getChecksums(version string) (map[string]string, error) {
  59. uri := formatString("https://github.com/{owner}/{repo}/releases/download/{version}/{repo}_{version}_checksums.txt", map[string]string{
  60. "version": version,
  61. "owner": owner,
  62. "repo": repo,
  63. })
  64. res, err := goreq.Request{
  65. Uri: uri,
  66. MaxRedirects: 10,
  67. RedirectHeaders: true,
  68. }.Do()
  69. if err != nil {
  70. return nil, err
  71. }
  72. defer res.Body.Close()
  73. scanner := bufio.NewScanner(res.Body)
  74. m := make(map[string]string, 6)
  75. for scanner.Scan() {
  76. var filename, sha256sum string
  77. _, err := fmt.Sscanf(scanner.Text(), "%s\t%s", &sha256sum, &filename)
  78. if err != nil {
  79. continue
  80. }
  81. m[filename] = sha256sum
  82. }
  83. return m, nil
  84. }
  85. func doUpdate(version string) (err error) {
  86. if version == "" {
  87. version, err = getLatestVersion()
  88. if err != nil {
  89. return err
  90. }
  91. }
  92. arch := runtime.GOARCH
  93. if runtime.GOOS == "linux" && arch == "arm" {
  94. arch += "v7"
  95. }
  96. filename := fmt.Sprintf("%s_%s_%s_%s.tar.gz", repo, version, runtime.GOOS, arch)
  97. log.Printf("update file: %s", filename)
  98. checksums, err := getChecksums(version)
  99. if err != nil {
  100. return err
  101. }
  102. checksum, ok := checksums[filename]
  103. if !ok {
  104. return fmt.Errorf("checksums not found for file: %s", filename)
  105. }
  106. // fixed get latest version
  107. uri := formatString("https://github.com/{owner}/{repo}/releases/download/{version}/{filename}", map[string]string{
  108. "version": version,
  109. "owner": owner,
  110. "repo": repo,
  111. "filename": filename,
  112. })
  113. log.Printf("update url: %s", uri)
  114. res, err := goreq.Request{
  115. Uri: uri,
  116. MaxRedirects: 10,
  117. RedirectHeaders: true,
  118. }.Do()
  119. if err != nil {
  120. return err
  121. }
  122. defer res.Body.Close()
  123. if res.StatusCode != 200 {
  124. err = fmt.Errorf("HTTP download error: [%d] %s", res.StatusCode, res.Status)
  125. return err
  126. }
  127. contentLength, err := strconv.Atoi(res.Header.Get("Content-Length"))
  128. if err != nil {
  129. return err
  130. }
  131. hasher := sha256.New()
  132. progressR := &ioprogress.Reader{
  133. Reader: res.Body,
  134. Size: int64(contentLength),
  135. DrawFunc: ioprogress.DrawTerminalf(os.Stdout, ioprogress.DrawTextFormatBytes),
  136. }
  137. tmpdir := makeTempDir()
  138. distPath := filepath.Join(tmpdir, "dist.tar.gz")
  139. f, err := os.Create(distPath)
  140. if err != nil {
  141. return err
  142. }
  143. writer := io.MultiWriter(f, hasher)
  144. io.Copy(writer, progressR)
  145. if err = f.Close(); err != nil {
  146. return err
  147. }
  148. realChecksum := hex.EncodeToString(hasher.Sum(nil))
  149. if realChecksum != checksum {
  150. return fmt.Errorf("update file checksum wrong, expected: %s, got: %s", checksum, realChecksum)
  151. }
  152. if err = archiver.TarGz.Open(distPath, tmpdir); err != nil {
  153. return err
  154. }
  155. log.Println("perform updating")
  156. err, _ = update.New().FromFile(filepath.Join(tmpdir, repo))
  157. return err
  158. }