diff --git a/.github/workflows/publish-go-tester-task.yml b/.github/workflows/publish-go-tester-task.yml index 69efdba05..3b5f871d0 100644 --- a/.github/workflows/publish-go-tester-task.yml +++ b/.github/workflows/publish-go-tester-task.yml @@ -132,9 +132,9 @@ jobs: - name: Build the Agent for macos env: - MACOSX_DEPLOYMENT_TARGET: 10.11 # minimum supported version for mac - CGO_CFLAGS: -mmacosx-version-min=10.11 - CGO_LDFLAGS: -mmacosx-version-min=10.11 + MACOSX_DEPLOYMENT_TARGET: 10.15 # minimum supported version for mac + CGO_CFLAGS: -mmacosx-version-min=10.15 + CGO_LDFLAGS: -mmacosx-version-min=10.15 run: task go:build if: runner.os == 'macOS' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9e6837e56..9ac64657f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: env: # As defined by the Taskfile's PROJECT_NAME variable PROJECT_NAME: arduino-create-agent - TARGET: "/CreateAgent/Stable" + TARGET: "/CreateAgent/Stable/" OLD_TARGET: "/CreateBridge/" # compatibility with older releases (we can't change config.ini) VERSION_TARGET: "arduino-create-static/agent-metadata/" AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -26,6 +26,8 @@ env: jobs: # The build job is responsible for: configuring the environment, testing and compiling process build: + outputs: + prerelease: ${{ steps.prerelease.outputs.IS_PRE }} strategy: matrix: os: [ubuntu-20.04, windows-2019, macos-12] @@ -110,9 +112,9 @@ jobs: - name: Build the Agent for macos env: - MACOSX_DEPLOYMENT_TARGET: 10.11 # minimum supported version for mac - CGO_CFLAGS: -mmacosx-version-min=10.11 - CGO_LDFLAGS: -mmacosx-version-min=10.11 + MACOSX_DEPLOYMENT_TARGET: 10.15 # minimum supported version for mac + CGO_CFLAGS: -mmacosx-version-min=10.15 + CGO_LDFLAGS: -mmacosx-version-min=10.15 run: task go:build if: matrix.os == 'macos-12' @@ -121,6 +123,14 @@ jobs: run: go-selfupdate ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} if: matrix.arch != '-386' && steps.prerelease.outputs.IS_PRE != 'true' + # for now we do not distribute m1 build, this is a workaround for now + - name: Copy autoupdate file for darwin-arm64 (m1 arch) + working-directory: public/ + run: | + cp darwin-amd64.json darwin-arm64.json + cp ${TAG_VERSION}/darwin-amd64.gz ${TAG_VERSION}/darwin-arm64.gz + if: matrix.os == 'macos-12' && steps.prerelease.outputs.IS_PRE != 'true' + - name: Create autoupdate files for win32 run: go-selfupdate -platform windows${{ matrix.arch }} ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} if: matrix.arch == '-386' && matrix.os == 'windows-2019' && steps.prerelease.outputs.IS_PRE != 'true' @@ -142,6 +152,11 @@ jobs: create-macos-bundle: needs: build + # for not they are exaclty the same + strategy: + matrix: + arch: [amd64, arm64] + runs-on: macos-12 env: EXE_PATH: "skel/ArduinoCreateAgent.app/Contents/MacOS/" @@ -156,7 +171,7 @@ jobs: - name: Download artifact uses: actions/download-artifact@v3 with: - name: ${{ env.PROJECT_NAME }}-macos-12-amd64 + name: ${{ env.PROJECT_NAME }}-macos-12-amd64 # if we want to support darwin-arm64 in the future for real this has to change. path: ${{ env.EXE_PATH }} - name: Remove placeholder file @@ -195,31 +210,37 @@ jobs: EOF - name: Tar bundle to keep permissions - run: tar -cvf ArduinoCreateAgent.app.tar -C skel/ . + run: tar -cvf ArduinoCreateAgent.app_${{ matrix.arch }}.tar -C skel/ . - name: Upload artifacts uses: actions/upload-artifact@v3 with: if-no-files-found: error - name: ArduinoCreateAgent.app - path: ArduinoCreateAgent.app.tar + name: ArduinoCreateAgent.app_${{ matrix.arch }} + path: ArduinoCreateAgent.app_${{ matrix.arch }}.tar - # The notarize-macos job will download the macos bundle from the previous job, sign, notarize and re-upload it. + # The notarize-macos job will download the macos bundle from the previous job, sign, notarize and re-upload it, uploading it also on s3 download servers for the autoupdate. notarize-macos: name: Notarize bundle + + # for not they are exaclty the same + strategy: + matrix: + arch: [amd64, arm64] + runs-on: macos-12 env: GON_PATH: ${{ github.workspace }}/gon - needs: create-macos-bundle + needs: [build, create-macos-bundle] steps: - name: Download artifact uses: actions/download-artifact@v3 with: - name: ArduinoCreateAgent.app + name: ArduinoCreateAgent.app_${{ matrix.arch }} - name: un-Tar bundle - run: tar -xvf ArduinoCreateAgent.app.tar + run: tar -xvf ArduinoCreateAgent.app_${{ matrix.arch }}.tar - name: Import Code-Signing Certificates run: | @@ -270,18 +291,37 @@ jobs: # Ask Gon for zip output to force notarization process to take place. # The CI will upload the zip output zip { - output_path = "ArduinoCreateAgent.app_notarized.zip" + output_path = "ArduinoCreateAgent.app_${{ matrix.arch }}_notarized.zip" } EOF - name: Sign and notarize binary run: gon -log-level=debug -log-json "${{ env.GON_CONFIG_PATH }}" + + - name: Upload autoupdate bundle to Arduino downloads servers + run: aws s3 cp ArduinoCreateAgent.app_${{ matrix.arch }}_notarized.zip s3://${{ secrets.DOWNLOADS_BUCKET }}${{ env.TARGET }}${GITHUB_REF/refs\/tags\//}/ # the version should be created in th the build job + if: ${{ needs.build.outputs.prerelease != 'true' }} + + - name: Generate json file used for the new autoupdate + run: | + cat > darwin-${{ matrix.arch }}-bundle.json </tmp/semver && chmod +x /tmp/semver - if [[ $(/tmp/semver get prerel ${GITHUB_REF/refs\/tags\//}) ]]; then echo "IS_PRE=true" >> $GITHUB_OUTPUT; fi - # mandatory step because upload-release-action does not support multiple folders - name: prepare artifacts for the release run: | @@ -562,7 +595,7 @@ jobs: release_name: ${{ github.ref }} body: ${{ steps.release_body.outputs.RBODY}} draft: false - prerelease: ${{ steps.prerelease.outputs.IS_PRE }} + prerelease: ${{ needs.build.outputs.prerelease }} - name: Upload release files on Github uses: svenstaro/upload-release-action@v2 @@ -574,10 +607,10 @@ jobs: - name: Upload release files on Arduino downloads servers run: aws s3 sync release/ s3://${{ secrets.DOWNLOADS_BUCKET }}${{ env.TARGET }} - if: steps.prerelease.outputs.IS_PRE != 'true' + if: ${{ needs.build.outputs.prerelease != 'true' }} - name: Update version file (used by frontend to trigger autoupdate and create filename) run: | echo {\"Version\": \"${GITHUB_REF##*/}\"} > /tmp/agent-version.json aws s3 cp /tmp/agent-version.json s3://${{ env.VERSION_TARGET }} - if: steps.prerelease.outputs.IS_PRE != 'true' + if: ${{ needs.build.outputs.prerelease != 'true' }} diff --git a/go.mod b/go.mod index 8f84b2819..ed6eb8fee 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/gin-gonic/gin v1.8.1 github.com/go-ini/ini v1.62.0 github.com/googollee/go-socket.io v0.0.0-20181101151912-c8aeb1ed9b49 - github.com/kr/binarydist v0.1.0 github.com/mattn/go-shellwords v1.0.12 github.com/mitchellh/go-homedir v1.1.0 github.com/oleksandr/bonjour v0.0.0-20210301155756-30f43c61b915 @@ -56,6 +55,7 @@ require ( github.com/juju/errors v0.0.0-20200330140219-3fe23663418f // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.15.13 // indirect + github.com/kr/binarydist v0.1.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect diff --git a/systray/exec_darwin.go b/systray/exec_darwin.go new file mode 100644 index 000000000..ba66e0c39 --- /dev/null +++ b/systray/exec_darwin.go @@ -0,0 +1,74 @@ +package systray + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Cocoa +#import + +char **makeCharArray(int size) { + return calloc(sizeof(char*), size); +} + +void setCharArray(char **a, int n, char *s) { + a[n] = s; +} + +void freeCharArray(char **a, int size) { + int i; + for (i = 0; i < size; i++) { + free(a[i]); + } + free(a); +} + +void runApplication(const char *path, const char **argv, int argc) { + NSMutableArray *stringArray = [NSMutableArray array]; + for (int i=0; i *arguments = [NSArray arrayWithArray:stringArray]; + + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + NSURL *url = [NSURL fileURLWithPath:@(path) isDirectory:NO]; + + NSWorkspaceOpenConfiguration* configuration = [NSWorkspaceOpenConfiguration new]; + //[configuration setEnvironment:env]; + [configuration setPromptsUserIfNeeded:YES]; + [configuration setCreatesNewApplicationInstance:YES]; + [configuration setArguments:arguments]; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [ws openApplicationAtURL:url configuration:configuration completionHandler:^(NSRunningApplication* app, NSError* error) { + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); +} +*/ +import "C" +import ( + "os/exec" + "path/filepath" + + "github.com/sirupsen/logrus" +) + +func execApp(path string, args ...string) error { + if filepath.Ext(path) != ".app" { + // If not .app, fallback to standard process execution + logrus.WithField("path", path).WithField("args", args).Info("Running new app with os/exec.Exec") + cmd := exec.Command(path, args...) + return cmd.Start() + } + + logrus.WithField("path", path).WithField("args", args).Info("Running new app with openApplicationAtURL") + argc := C.int(len(args)) + argv := C.makeCharArray(argc) + for i, arg := range args { + C.setCharArray(argv, C.int(i), C.CString(arg)) + } + + C.runApplication(C.CString(path), argv, argc) + + C.freeCharArray(argv, argc) + return nil +} diff --git a/systray/exec_default.go b/systray/exec_default.go new file mode 100644 index 000000000..ea28618b5 --- /dev/null +++ b/systray/exec_default.go @@ -0,0 +1,26 @@ +// Copyright 2022 Arduino SA +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +//go:build !darwin + +package systray + +import "os/exec" + +// default execApp from golang +func execApp(path string, args ...string) error { + cmd := exec.Command(path, args...) + return cmd.Start() +} diff --git a/systray/systray.go b/systray/systray.go index 85656bbc9..4df4fadbc 100644 --- a/systray/systray.go +++ b/systray/systray.go @@ -18,7 +18,6 @@ package systray import ( "fmt" "os" - "os/exec" "strings" "github.com/arduino/go-paths-helper" @@ -69,8 +68,7 @@ func (s *Systray) Restart() { } // Launch executable - cmd := exec.Command(s.path, args...) - err := cmd.Start() + err := execApp(s.path, args...) if err != nil { log.Printf("Error restarting process: %v\n", err) return diff --git a/update.go b/update.go index 21569f36c..33c028bce 100644 --- a/update.go +++ b/update.go @@ -35,11 +35,15 @@ import ( ) func updateHandler(c *gin.Context) { - restartPath, err := updater.CheckForUpdates(version, *updateURL, *updateURL, *appName) + restartPath, err := updater.CheckForUpdates(version, *updateURL, *appName) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{"success": "Please wait a moment while the agent reboots itself"}) - Systray.RestartWith(restartPath) + if restartPath == "quit" { + Systray.Quit() + } else { + Systray.RestartWith(restartPath) + } } diff --git a/updater/updater.go b/updater/updater.go index b15afe3f8..db4e5454f 100644 --- a/updater/updater.go +++ b/updater/updater.go @@ -22,7 +22,10 @@ import ( "fmt" "io" "net/http" + "os" + "path/filepath" "runtime" + "strings" log "github.com/sirupsen/logrus" ) @@ -36,16 +39,16 @@ func Start(src string) string { // CheckForUpdates checks if there is a new version of the binary available and // if so downloads it. -func CheckForUpdates(currentVersion string, updateAPIURL, updateBinURL string, cmdName string) (string, error) { - return checkForUpdates(currentVersion, updateAPIURL, updateBinURL, cmdName) +func CheckForUpdates(currentVersion string, updateURL string, cmdName string) (string, error) { + return checkForUpdates(currentVersion, updateURL, cmdName) } const ( plat = runtime.GOOS + "-" + runtime.GOARCH ) -func fetchInfo(updateAPIURL string, cmdName string) (*availableUpdateInfo, error) { - r, err := fetch(updateAPIURL + cmdName + "/" + plat + ".json") +func fetchInfo(updateAPIURL string) (*availableUpdateInfo, error) { + r, err := fetch(updateAPIURL) if err != nil { return nil, err } @@ -77,3 +80,33 @@ func fetch(url string) (io.ReadCloser, error) { } return resp.Body, nil } + +// addTempSuffixToPath adds the "-temp" suffix to the path to an executable file (a ".exe" extension is replaced with "-temp.exe") +func addTempSuffixToPath(path string) string { + if filepath.Ext(path) == "exe" { + path = strings.Replace(path, ".exe", "-temp.exe", -1) + } else { + path = path + "-temp" + } + + return path +} + +// removeTempSuffixFromPath removes "-temp" suffix from the path to an executable file (a "-temp.exe" extension is replaced with ".exe") +func removeTempSuffixFromPath(path string) string { + return strings.Replace(path, "-temp", "", -1) +} + +func copyExe(from, to string) error { + data, err := os.ReadFile(from) + if err != nil { + log.Println("Cannot read file: ", from) + return err + } + err = os.WriteFile(to, data, 0755) + if err != nil { + log.Println("Cannot write file: ", to) + return err + } + return nil +} diff --git a/updater/updater_darwin.go b/updater/updater_darwin.go index 45c67e5c7..f0bc6e5ed 100644 --- a/updater/updater_darwin.go +++ b/updater/updater_darwin.go @@ -15,10 +15,148 @@ package updater +import ( + "bytes" + "context" + "crypto/sha256" + "fmt" + "io" + "os" + "runtime" + "strings" + + "github.com/arduino/go-paths-helper" + "github.com/codeclysm/extract/v3" + "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" +) + func start(src string) string { + if strings.Contains(src, "-temp") { + // This is required to transition from the previous auto-update system to the new one. + // Updating from version <=1.2.7 will produce the `ArduinoCreateAgent-temp` file and we should + // complete the upgrade by copying the `ArduinoCreateAgent-temp` executable back to the original. + // + // This procedure will be automatically skipped starting from version >1.2.7. + + newPath := removeTempSuffixFromPath(src) + if err := copyExe(src, newPath); err != nil { + log.Println("Copy error: ", err) + panic(err) + } + return newPath + } + return "" } -func checkForUpdates(currentVersion string, updateAPIURL, updateBinURL string, cmdName string) (string, error) { - return "", nil +func checkForUpdates(currentVersion string, updateURL string, cmdName string) (string, error) { + executablePath, err := os.Executable() + if err != nil { + return "", fmt.Errorf("could not app path: %w", err) + } + currentAppPath := paths.New(executablePath).Parent().Parent().Parent() + if currentAppPath.Ext() != ".app" { + return "", fmt.Errorf("could not find app root in %s", executablePath) + } + oldAppPath := currentAppPath.Parent().Join("ArdiunoCreateAgent.old.app") + if oldAppPath.Exist() { + return "", fmt.Errorf("temp app already exists: %s, cannot update", oldAppPath) + } + + // Fetch information about updates + + // updateURL: "https://downloads.arduino.cc/" + // cmdName: "CreateAgent/Stable" + // plat: "darwin-amd64" + // info URL: "https://downloads.arduino.cc/CreateAgent/Stable/darwin-amd64-bundle.json" + infoURL := updateURL + cmdName + "/" + plat + "-bundle.json" + info, err := fetchInfo(infoURL) + if err != nil { + return "", err + } + if info.Version == currentVersion { + // No updates available, bye bye + return "", nil + } + + tmp := paths.TempDir().Join("arduino-create-agent") + if err := tmp.MkdirAll(); err != nil { + return "", err + } + tmpZip := tmp.Join("update.zip") + tmpAppPath := tmp.Join("ArduinoCreateAgent-update.app") + defer tmp.RemoveAll() + + // Download the update. + + // updateURL: "https://downloads.arduino.cc/" + // cmdName == "CreateAgent/Stable" + // info.Version == "1.2.8" + // runtime.GOARCH: "amd64" + // downloadURL: "https://downloads.arduino.cc/CreateAgent/Stable/1.2.8/ArduinoCreateAgent.app_arm64_notarized.zip" + downloadURL := updateURL + cmdName + "/" + info.Version + "/ArduinoCreateAgent.app_" + runtime.GOARCH + "_notarized.zip" + logrus.WithField("url", downloadURL).Info("Downloading update") + download, err := fetch(downloadURL) + if err != nil { + return "", err + } + defer download.Close() + + f, err := tmpZip.Create() + if err != nil { + return "", err + } + defer f.Close() + + sha := sha256.New() + if _, err := io.Copy(io.MultiWriter(sha, f), download); err != nil { + return "", err + } + f.Close() + + // Check the hash + if s := sha.Sum(nil); !bytes.Equal(s, info.Sha256) { + return "", fmt.Errorf("bad hash: %s (expected %s)", s, info.Sha256) + } + + // Unzip the update + logrus.WithField("tmpDir", tmpAppPath).Info("Unzipping update") + if err := tmpAppPath.MkdirAll(); err != nil { + return "", fmt.Errorf("could not create tmp dir to unzip update: %w", err) + } + + f, err = tmpZip.Open() + if err != nil { + return "", fmt.Errorf("could not open archive for unzip: %w", err) + } + defer f.Close() + if err := extract.Archive(context.Background(), f, tmpAppPath.String(), nil); err != nil { + return "", fmt.Errorf("extracting archive: %w", err) + } + + // Rename current app as .old + logrus.WithField("from", currentAppPath).WithField("to", oldAppPath).Info("Renaming old app") + if err := currentAppPath.Rename(oldAppPath); err != nil { + return "", fmt.Errorf("could not rename old app as .old: %w", err) + } + + // Install new app + logrus.WithField("from", tmpAppPath).WithField("to", currentAppPath).Info("Copying updated app") + if err := tmpAppPath.CopyDirTo(currentAppPath); err != nil { + // Try rollback changes + _ = currentAppPath.RemoveAll() + _ = oldAppPath.Rename(currentAppPath) + return "", fmt.Errorf("could not install app: %w", err) + } + + // Remove old app + logrus.WithField("to", oldAppPath).Info("Removing old app") + _ = oldAppPath.RemoveAll() + + // Restart agent + logrus.WithField("path", currentAppPath).Info("Running new app") + + // Close old agent + return currentAppPath.String(), nil } diff --git a/updater/updater_default.go b/updater/updater_default.go index 4562907fc..2cf01b8ad 100644 --- a/updater/updater_default.go +++ b/updater/updater_default.go @@ -28,7 +28,6 @@ import ( "path/filepath" "strings" - "github.com/kr/binarydist" log "github.com/sirupsen/logrus" "gopkg.in/inconshreveable/go-update.v0" ) @@ -60,7 +59,6 @@ import ( // var errHashMismatch = errors.New("new file hash mismatch after patch") -var errDiffURLUndefined = errors.New("DiffURL is not defined, I cannot fetch and apply patch, reverting to full bin") var up = update.New() func start(src string) string { @@ -81,16 +79,14 @@ func start(src string) string { return "" } -func checkForUpdates(currentVersion string, updateAPIURL, updateBinURL string, cmdName string) (string, error) { +func checkForUpdates(currentVersion string, updateURL string, cmdName string) (string, error) { path, err := os.Executable() if err != nil { return "", err } var up = &Updater{ CurrentVersion: currentVersion, - APIURL: updateAPIURL, - BinURL: updateBinURL, - DiffURL: "", + UpdateURL: updateURL, Dir: "update/", CmdName: cmdName, } @@ -101,36 +97,6 @@ func checkForUpdates(currentVersion string, updateAPIURL, updateBinURL string, c return addTempSuffixToPath(path), nil } -func copyExe(from, to string) error { - data, err := os.ReadFile(from) - if err != nil { - log.Println("Cannot read file: ", from) - return err - } - err = os.WriteFile(to, data, 0755) - if err != nil { - log.Println("Cannot write file: ", to) - return err - } - return nil -} - -// addTempSuffixToPath adds the "-temp" suffix to the path to an executable file (a ".exe" extension is replaced with "-temp.exe") -func addTempSuffixToPath(path string) string { - if filepath.Ext(path) == "exe" { - path = strings.Replace(path, ".exe", "-temp.exe", -1) - } else { - path = path + "-temp" - } - - return path -} - -// removeTempSuffixFromPath removes "-temp" suffix from the path to an executable file (a "-temp.exe" extension is replaced with ".exe") -func removeTempSuffixFromPath(path string) string { - return strings.Replace(path, "-temp", "", -1) -} - // Updater is the configuration and runtime data for doing an update. // // Note that ApiURL, BinURL and DiffURL should have the same value if all files are available at the same location. @@ -139,9 +105,7 @@ func removeTempSuffixFromPath(path string) string { // // updater := &selfupdate.Updater{ // CurrentVersion: version, -// ApiURL: "http://updates.yourdomain.com/", -// BinURL: "http://updates.yourdownmain.com/", -// DiffURL: "http://updates.yourdomain.com/", +// UpdateURL: "http://updates.yourdomain.com/", // Dir: "update/", // CmdName: "myapp", // app name // } @@ -150,10 +114,8 @@ func removeTempSuffixFromPath(path string) string { // } type Updater struct { CurrentVersion string // Currently running version. - APIURL string // Base URL for API requests (json files). + UpdateURL string // Base URL for API requests (json files). CmdName string // Command name is appended to the ApiURL like http://apiurl/CmdName/. This represents one binary. - BinURL string // Base URL for full binary downloads. - DiffURL string // Base URL for diff downloads. Dir string // Directory to store selfupdate state. Info *availableUpdateInfo // Information about the available update. } @@ -183,31 +145,6 @@ func verifySha(bin []byte, sha []byte) bool { return bytes.Equal(h.Sum(nil), sha) } -func (u *Updater) fetchAndApplyPatch(old io.Reader) ([]byte, error) { - if u.DiffURL == "" { - return nil, errDiffURLUndefined - } - r, err := fetch(u.DiffURL + u.CmdName + "/" + u.CurrentVersion + "/" + u.Info.Version + "/" + plat) - if err != nil { - return nil, err - } - defer r.Close() - var buf bytes.Buffer - err = binarydist.Patch(old, &buf, r) - return buf.Bytes(), err -} - -func (u *Updater) fetchAndVerifyPatch(old io.Reader) ([]byte, error) { - bin, err := u.fetchAndApplyPatch(old) - if err != nil { - return nil, err - } - if !verifySha(bin, u.Info.Sha256) { - return nil, errHashMismatch - } - return bin, nil -} - func (u *Updater) fetchAndVerifyFullBin() ([]byte, error) { bin, err := u.fetchBin() if err != nil { @@ -221,7 +158,7 @@ func (u *Updater) fetchAndVerifyFullBin() ([]byte, error) { } func (u *Updater) fetchBin() ([]byte, error) { - r, err := fetch(u.BinURL + u.CmdName + "/" + u.Info.Version + "/" + plat + ".gz") + r, err := fetch(u.UpdateURL + u.CmdName + "/" + u.Info.Version + "/" + plat + ".gz") if err != nil { return nil, err } @@ -258,7 +195,8 @@ func (u *Updater) update() error { } defer old.Close() - info, err := fetchInfo(u.APIURL, u.CmdName) + infoURL := u.UpdateURL + u.CmdName + "/" + plat + ".json" + info, err := fetchInfo(infoURL) if err != nil { log.Println(err) return err @@ -267,26 +205,15 @@ func (u *Updater) update() error { if u.Info.Version == u.CurrentVersion { return nil } - bin, err := u.fetchAndVerifyPatch(old) - if err != nil { - switch err { - case errHashMismatch: - log.Println("update: hash mismatch from patched binary") - case errDiffURLUndefined: - log.Println("update: ", err) - default: - log.Println("update: patching binary, ", err) - } - bin, err = u.fetchAndVerifyFullBin() - if err != nil { - if err == errHashMismatch { - log.Println("update: hash mismatch from full binary") - } else { - log.Println("update: fetching full binary,", err) - } - return err + bin, err := u.fetchAndVerifyFullBin() + if err != nil { + if err == errHashMismatch { + log.Println("update: hash mismatch from full binary") + } else { + log.Println("update: fetching full binary,", err) } + return err } // close the old binary before installing because on windows