From fbb9bf89c045e96da00bfbc7f407758c87894e92 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 25 Feb 2013 16:26:40 -0500 Subject: MAINT-2155, DRTVWR-278: Make Mac updater honor app bundle name in .dmg. If you run "/Applications/Second Life Beta Materials.app", and the version manager directs you to update to a .dmg containing a release candidate whose app bundle name is "Second Life Viewer.app", update_install.py used to copy the contents of "Second Life Viewer.app" into "/Applications/Second Life Beta Materials.app". Changed it so that if the application bundle name differs, we leave the running app bundle alone, installing instead to the app bundle name from the .dmg. --- .../updater/scripts/darwin/update_install.py | 98 +++++++++++++++++----- 1 file changed, 75 insertions(+), 23 deletions(-) (limited to 'indra/viewer_components/updater/scripts/darwin') diff --git a/indra/viewer_components/updater/scripts/darwin/update_install.py b/indra/viewer_components/updater/scripts/darwin/update_install.py index 25efc2b0bc..2fc6fcdb29 100755 --- a/indra/viewer_components/updater/scripts/darwin/update_install.py +++ b/indra/viewer_components/updater/scripts/darwin/update_install.py @@ -139,7 +139,7 @@ def write_marker(markerfile, markertext): # **************************************************************************** # Main script logic # **************************************************************************** -def main(dmgfile, markerfile, markertext, appdir=None): +def main(dmgfile, markerfile, markertext): # Should we fail, we're supposed to write 'markertext' to 'markerfile'. # Wrap the fail() function so we do that. global fail @@ -179,18 +179,19 @@ def main(dmgfile, markerfile, markertext, appdir=None): # prepare for other cleanup with Janitor(LOGF) as janitor: - # Hopefully caller explicitly stated the viewer bundle to update. - # But if not, try to derive it from our own pathname. (The only - # trouble with that is that the old viewer might copy this script - # to a temp dir before running.) - if not appdir: - # Somewhat peculiarly, this script is currently packaged in - # Appname.app/Contents/MacOS with the viewer executable. But even if we - # decide to move it to Appname.app/Contents/Resources, we'll still find - # Appname.app two levels up from dirname(__file__). - appdir = os.path.abspath(os.path.join(os.path.dirname(__file__), - os.pardir, os.pardir)) + # Try to derive the name of the running viewer app bundle from our + # own pathname. (Hopefully the old viewer won't copy this script + # to a temp dir before running!) + # Somewhat peculiarly, this script is currently packaged in + # Appname.app/Contents/MacOS with the viewer executable. But even + # if we decide to move it to Appname.app/Contents/Resources, we'll + # still find Appname.app two levels up from dirname(__file__). + appdir = os.path.abspath(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir)) if not appdir.endswith(".app"): + # This can happen if either this script has been copied before + # being executed, or if it's in an unexpected place in the app + # bundle. fail(appdir + " is not an application directory") # We need to install into appdir's parent directory -- can we? @@ -260,15 +261,66 @@ def main(dmgfile, markerfile, markertext, appdir=None): # Here 'candidate' is the new viewer to install log("Found " + candidate) + + # This logic was changed to make Mac updates behave more like + # Windows. Most of the time, the user doesn't change the name of + # the app bundle on our .dmg installer (e.g. "Second Life Beta + # Viewer.app"). Most of the time, the version manager directs a + # given viewer to update to another .dmg containing an app bundle + # with THE SAME name. In that case, everything behaves as usual. + + # The case that was changed is when the version manager offers (or + # mandates) an update to a .dmg containing a different app bundle + # name. This can happen, for instance, to a user who's downloaded + # a "project beta" viewer, and the project subsequently publishes + # a Release Candidate viewer. Say the project beta's app bundle + # name is something like "Second Life Beta Neato.app". Anyone + # launching that viewer will be offered an update to the + # corresponding Release Candidate viewer -- which will be built as + # a release viewer, with app bundle name "Second Life Viewer.app". + + # On Windows, we run the NSIS installer, which will update/replace + # the embedded install directory name, e.g. Second Life Viewer. + # But the Mac installer used to locate the app bundle name in the + # mounted .dmg file, then ignore that name, copying its contents + # into the app bundle directory of the running viewer. That is, + # we'd install the Release Candidate from the .dmg's "Second + # Life.app" into "/Applications/Second Life Beta Neato.app". This + # is undesired behavior. + + # Instead, having found the app bundle name on the mounted .dmg, + # we try to install that app bundle name into the parent directory + # of the running app bundle. + + # Are we installing a different app bundle name? If so, call it + # out, both in the log and for the user -- this is an odd case. + # (Presumably they've already agreed to a similar notification in + # the viewer before the viewer launched this script, but still.) + bundlename = os.path.basename(candidate) + if os.path.basename(appdir) == bundlename: + # updating the running app bundle, which we KNOW exists + appexists = True + else: + # installing some other app bundle + newapp = os.path.join(installdir, bundlename) + appexists = os.path.exists(newapp) + message = "Note: %s %s %s" % \ + (appdir, "updating" if appexists else "installing new", newapp) + status(message) + # okay, we have no further need of the name of the running app + # bundle. + appdir = newapp + status("Preparing to copy files...") - # move old viewer to temp location in case copy from .dmg fails - aside = os.path.join(tempdir, os.path.basename(appdir)) - log("mv %r %r" % (appdir, aside)) - # Use shutil.move() instead of os.rename(). move() first tries - # os.rename(), but falls back to shutil.copytree() if the dest is - # on a different filesystem. - shutil.move(appdir, aside) + if appexists: + # move old viewer to temp location in case copy from .dmg fails + aside = os.path.join(tempdir, os.path.basename(appdir)) + log("mv %r %r" % (appdir, aside)) + # Use shutil.move() instead of os.rename(). move() first tries + # os.rename(), but falls back to shutil.copytree() if the dest is + # on a different filesystem. + shutil.move(appdir, aside) status("Copying files...") @@ -282,8 +334,9 @@ def main(dmgfile, markerfile, markertext, appdir=None): except Exception, err: # copy failed -- try to restore previous viewer before crumping type, value, traceback = sys.exc_info() - log("exception response: mv %r %r" % (aside, appdir)) - shutil.move(aside, appdir) + if appexists: + log("exception response: mv %r %r" % (aside, appdir)) + shutil.move(aside, appdir) # let our previously-set sys.excepthook handle this raise type, value, traceback @@ -316,6 +369,5 @@ if __name__ == "__main__": # We expect this script to be invoked with: # - the pathname to the .dmg we intend to install; # - the pathname to an update-error marker file to create on failure; - # - the content to write into the marker file; - # - optionally, the pathname of the Second Life viewer to update. + # - the content to write into the marker file. main(*sys.argv[1:]) -- cgit v1.2.3