Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checkForUpdatesInBackground not installing new version #839

Closed
mherrmann opened this issue Jul 13, 2016 · 11 comments
Closed

checkForUpdatesInBackground not installing new version #839

mherrmann opened this issue Jul 13, 2016 · 11 comments

Comments

@mherrmann
Copy link

I have a non-standard but I believe very powerful setup: I'm using PyQt to develop a cross-platform Qt app. I want to use Sparkle to update my app on OS X.

I'm able to call Sparkle's checkForUpdates from my code, with the expected behaviour: Sparkle checks for updates, prompts the user and installs the update. After my app is relaunched, the new version is active.

I now want updates to be performed in the background, without any GUI. I do this by setting automaticallyChecksForUpdates and automaticallyDownloadsUpdates to YES. Then I call checkForUpdatesInBackground. This does download and extract the update. The problem is that when I start my app the next time, it still uses the old version rather than the new one.

I see the following console output from Sparkle:

Sparkle: ===== fman =====
Sparkle: WARNING: Serving updates over HTTP without signing them with a DSA key is deprecated and may not be possible in a future release. Please serve your updates over https, or sign them with a DSA key, or do both. See Sparkle's documentation for more information.
Sparkle: Extracting using '/usr/bin/ditto' '-x' '-k' '-' < '/Users/michael/Library/Caches/io.fman.fman/Sparkle/fman 2.0.0/fman200.zip' '/Users/michael/Library/Caches/io.fman.fman/Sparkle/fman 2.0.0'

The location mentioned in the output, /Users/michael/Library/Caches/io.fman.fman/Sparkle/fman 2.0.0, contains a working copy of the "new" version of my app. So Sparkle seems to be able to do everything successfully, except the final step of installing the new version of my app over the old one.

Does anybody have an idea as to what could be going wrong, or what I could do to debug the issue?

@zorgiepoo
Copy link
Member

Sparkle tries to perform the install when your app quits. In particular when it receives the NSApplicationWillTerminateNotification event.

Taken from the code in SUAutomaticUpdateDriver.m:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil];

When you quit your app, the Autoupdate application should be launched in the background (could verify this in Activity Monitor filtering for Autoupdate). If your app doesn't receive the notification, then neither will Sparkle, though.

By the way, installs are cancelled when the system is being shut off, and automatic downloads are disabled when the user needs root privileges to install the new update.

@mherrmann
Copy link
Author

Thank you very much for the quick reply. Using your information, I did some searching and suspect that what I am seeing is #232. I'll post an update here once I know for sure.

@mherrmann
Copy link
Author

mherrmann commented Jul 18, 2016

By manually posting a NSApplicationWillTerminateNotification similar to #232, I am now able to have Sparkle successfully update my application. Functionally, this is all I need. However, I see the following ugly error on stdout when my app is closed and Sparkle applies the update:

Sparkle: ===== Autoupdate =====
Sparkle: mdimporting
Sparkle: ===== Autoupdate =====
Sparkle: Searched /Users/michael/Library/Caches/io.fman.fman/Sparkle/fman 0.0.2 for fman.(app|pkg)
Sparkle: Installation Error: Error Domain=SUSparkleErrorDomain Code=4002 "Couldn't find an appropriate update in the downloaded package." UserInfo={NSLocalizedDescription=Couldn't find an appropriate update in the downloaded package.}

After being stumped for a while, I realised that Autoupdate is being run twice. The above output shows that it runs successfully the first time around. But by the second time, the downloaded / extracted files have already been deleted so it says Couldn't find an appropriate update....

Do you maybe happen to know why Autoupdate is being run twice, or what I could do to stop this behaviour? (I'm sure I'm only posting NSApplicationWillTerminateNotification once.)

@mherrmann
Copy link
Author

While I certainly broadcast NSApplicationWillTerminateNotification only once, the notification does get posted twice when an update is being installed. The first time is from me. I am now trying to find out more about the second time.

@mherrmann
Copy link
Author

I found a way to fix the problem with NSApplicationWillTerminateNotification being broadcast twice: Instead of manually posting this notification, it is better to call [NSApp terminate:sender]. It seems that then Cocoa realises that the app is being shut down, and does not broadcast the notification a second time.

I will publish a blog post on https://fman.io/blog in the coming days with a summary of my findings. It would be great if I could then still comment on the issue, to provide a direct link to the post for others who may be interested. Other than that, this issue can be closed from my point of view.

Thank you for the support @zorgiepoo! And of course, thanks to the entire Sparkle team for creating such an awesome framework.

@mherrmann
Copy link
Author

I wrote up my solution at https://fman.io/blog/codesigning-and-automatic-updates-for-pyqt-apps/, in the hope that it will be useful for other PyQt developers.

@kornelski
Copy link
Member

Cool, thanks!

@duolabmeng6
Copy link

I wrote up my solution at https://fman.io/blog/codesigning-and-automatic-updates-for-pyqt-apps/, in the hope that it will be useful for other PyQt developers.

Thank you very much for your solution, but can you be more detailed? I can't reproduce your solution. I also want to automatically update the application in pyqt, but I won't configure it. I'm very sorry. Can you make a demo? Thank you very much

@duolabmeng6
Copy link

I wrote up my solution at https://fman.io/blog/codesigning-and-automatic-updates-for-pyqt-apps/, in the hope that it will be useful for other PyQt developers.

import sys

from PySide6.QtCore import QRect
from PySide6.QtWidgets import *


class MainWin(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(QRect(100, 100, 400, 300))
        self.setWindowTitle('Sparkle Test')
        # 创建按钮
        self.btn = QPushButton(self)
        self.btn.setText('Check for Updates')
        self.btn.setGeometry(QRect(20, 20, 200, 40))
        self.btn.show()
        # self.aboutToQuit.connect(about_to_quit)

        self.show()


QT_APP = QApplication([])

APPCAST_URL = 'http://127.0.0.1:8000/SampleAppcast.xml'
SPARKLE_PATH = '/Users/chensuilong/Downloads/Sparkle-2.2.0/Sparkle.framework'

from objc import pathForFramework, loadBundle

sparkle_path = pathForFramework(SPARKLE_PATH)
objc_namespace = dict()
loadBundle('Sparkle', objc_namespace, bundle_path=sparkle_path)


def about_to_quit():
    print('about to quit')
    # 见 https://github.com/sparkle-project/Sparkle/issues/839
    objc_namespace['NSApplication'].sharedApplication().terminate_(None)


QT_APP.aboutToQuit.connect(about_to_quit)
sparkle = objc_namespace['SUUpdater'].sharedUpdater()
sparkle.setAutomaticallyChecksForUpdates_(True)
sparkle.setAutomaticallyDownloadsUpdates_(True)
NSURL = objc_namespace['NSURL']
sparkle.setFeedURL_(NSURL.URLWithString_(APPCAST_URL))
sparkle.checkForUpdatesInBackground()

window = MainWin()
window.btn.clicked.connect(about_to_quit)

sys.exit(QT_APP.exec())

@duolabmeng6
Copy link

https://github.com/sparkle-project/Sparkle/releases/tag/2.2.0

I downloaded version 2.2.0 from the official website.
Write the above script according to your tutorial
But it didn't work.
I hope to get your help.

@zorgiepoo
Copy link
Member

This issue has already been resolved. Discussion for helping you can be in #2205

For future reference regarding this original issue you no longer need to do anything special for app termination in Sparkle 2. The cocoa termination event isn't needed/observed anymore.

Also:

sparkle.setAutomaticallyChecksForUpdates_(True)
sparkle.setAutomaticallyDownloadsUpdates_(True)
NSURL = objc_namespace['NSURL']
sparkle.setFeedURL_(NSURL.URLWithString_(APPCAST_URL))
sparkle.checkForUpdatesInBackground()

None of these things should be done in code like this. The feed URL should be specified in the app's Info.plist using the SUFeedURL key. The SUAutomaticallyUpdate can be set in the Info.plist if you want automatic downloading/installing of updates. SUEnableAutomaticChecks can be set if you don't want Sparkle to ask for user permission to check for updates. Sparkle checks for updates in the background automatically on a scheduled interval. More info here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants