|
17 | 17 |
|
18 | 18 | package updater
|
19 | 19 |
|
| 20 | +import ( |
| 21 | + "bytes" |
| 22 | + "context" |
| 23 | + "crypto/sha256" |
| 24 | + "fmt" |
| 25 | + "io" |
| 26 | + "os" |
| 27 | + |
| 28 | + "github.com/arduino/go-paths-helper" |
| 29 | + "github.com/codeclysm/extract/v3" |
| 30 | +) |
| 31 | + |
20 | 32 | func start(src string) string {
|
21 | 33 | return ""
|
22 | 34 | }
|
23 | 35 |
|
24 | 36 | func checkForUpdates(currentVersion string, updateAPIURL, updateBinURL string, cmdName string) (string, error) {
|
25 |
| - return "", nil |
| 37 | + executablePath, err := os.Executable() |
| 38 | + if err != nil { |
| 39 | + return "", fmt.Errorf("could not app path: %w", err) |
| 40 | + } |
| 41 | + currentAppPath := paths.New(executablePath).Parent().Parent().Parent() |
| 42 | + if currentAppPath.Ext() != ".app" { |
| 43 | + return "", fmt.Errorf("could not find app root in %s", executablePath) |
| 44 | + } |
| 45 | + oldAppPath := currentAppPath.Parent().Join("ArdiunoCreateAgent.old.app") |
| 46 | + if oldAppPath.Exist() { |
| 47 | + return "", fmt.Errorf("temp app already exists: %s, cannot update", oldAppPath) |
| 48 | + } |
| 49 | + |
| 50 | + // Fetch information about updates |
| 51 | + info, err := fetchInfo(updateAPIURL, cmdName) |
| 52 | + if err != nil { |
| 53 | + return "", err |
| 54 | + } |
| 55 | + if info.Version == currentVersion { |
| 56 | + // No updates available, bye bye |
| 57 | + return "", nil |
| 58 | + } |
| 59 | + |
| 60 | + tmp := paths.TempDir().Join("arduino-create-agent") |
| 61 | + tmpZip := tmp.Join("update.zip") |
| 62 | + tmpAppPath := tmp.Join("ArduinoCreateAgent-update.app") |
| 63 | + defer tmp.RemoveAll() |
| 64 | + |
| 65 | + // Download the update. |
| 66 | + download, err := fetch(updateBinURL + cmdName + "/" + plat + "/" + info.Version + "/" + cmdName) |
| 67 | + if err != nil { |
| 68 | + return "", err |
| 69 | + } |
| 70 | + defer download.Close() |
| 71 | + |
| 72 | + f, err := tmpZip.Create() |
| 73 | + if err != nil { |
| 74 | + return "", err |
| 75 | + } |
| 76 | + defer f.Close() |
| 77 | + |
| 78 | + sha := sha256.New() |
| 79 | + if _, err := io.Copy(io.MultiWriter(sha, f), download); err != nil { |
| 80 | + return "", err |
| 81 | + } |
| 82 | + f.Close() |
| 83 | + |
| 84 | + // Check the hash |
| 85 | + if s := sha.Sum(nil); !bytes.Equal(s, info.Sha256) { |
| 86 | + return "", fmt.Errorf("bad hash: %s (expected %s)", s, info.Sha256) |
| 87 | + } |
| 88 | + |
| 89 | + // Unzip the update |
| 90 | + if err := tmpAppPath.MkdirAll(); err != nil { |
| 91 | + return "", fmt.Errorf("could not create tmp dir to unzip update: %w", err) |
| 92 | + } |
| 93 | + |
| 94 | + f, err = tmpZip.Open() |
| 95 | + if err != nil { |
| 96 | + return "", fmt.Errorf("could not open archive for unzip: %w", err) |
| 97 | + } |
| 98 | + defer f.Close() |
| 99 | + if err := extract.Archive(context.Background(), f, tmpAppPath.String(), nil); err != nil { |
| 100 | + return "", fmt.Errorf("extracting archive: %w", err) |
| 101 | + } |
| 102 | + |
| 103 | + // Rename current app as .old |
| 104 | + if err := currentAppPath.Rename(oldAppPath); err != nil { |
| 105 | + return "", fmt.Errorf("could not rename old app as .old: %w", err) |
| 106 | + } |
| 107 | + |
| 108 | + // Install new app |
| 109 | + if err := tmpAppPath.CopyDirTo(currentAppPath); err != nil { |
| 110 | + // Try rollback changes |
| 111 | + _ = currentAppPath.RemoveAll() |
| 112 | + _ = oldAppPath.Rename(currentAppPath) |
| 113 | + return "", fmt.Errorf("could not install app: %w", err) |
| 114 | + } |
| 115 | + |
| 116 | + // Remove old app |
| 117 | + _ = oldAppPath.RemoveAll() |
| 118 | + |
| 119 | + // Restart agent |
| 120 | + return currentAppPath.Join("Contents", "MacOS", "Arduino_Create_Agent").String(), nil |
26 | 121 | }
|
0 commit comments