コード例 #1
0
    def checkVersion_(self, mountPoint):
        LogDebug(u"checkVersion:%@", mountPoint)

        # We're now examining InstallESD.dmg for 10.7/10.8, BaseSystem.dmg for
        # 10.9, or a system image.
        name, version, build = IEDUtil.readSystemVersion_(mountPoint)
        if self.baseSystemMountedFromPath:
            self.dmgHelper.detach_selector_(self.baseSystemMountedFromPath,
                                            self.handleDetachResult_)
        installerVersion = tuple(int(x) for x in version.split(u"."))
        runningVersion = tuple(
            int(x) for x in platform.mac_ver()[0].split(u"."))
        LogNotice(u"Found source: %@ %@ %@", name, version, build)
        if installerVersion[:2] != runningVersion[:2]:
            self.delegate.ejectingSource()
            self.dmgHelper.detachAll_(self.rejectSource_)
            return
        LogNotice(u"Accepted source %@: %@ %@ %@", self.newSourcePath, name,
                  version, build)
        self._source = self.newSourcePath
        self.installerName = name
        self.installerVersion = version
        self.installerBuild = build
        info = {
            u"name": name,
            u"version": version,
            u"build": build,
            u"template": self.loadImageTemplate_(mountPoint),
            u"sourceType": self.sourceType,
        }
        self.delegate.sourceSucceeded_(info)
        # There's no reason to keep the dmg mounted if it's not an installer.
        if self.sourceType == IEDWorkflow.SYSTEM_IMAGE:
            self.dmgHelper.detachAll_(self.ejectSystemImage_)
コード例 #2
0
    def nextTask(self):
        LogDebug("nextTask, currentTask == %@", self.currentTask)

        if self.currentTask:
            if self.currentTask["phases"]:
                for phase in self.currentTask["phases"]:
                    if not phase.get("optional", False):
                        details = NSString.stringWithFormat_(
                            "Phases remaining: %@", self.currentTask["phases"])
                        self.fail_details_("Task finished prematurely",
                                           details)
                        return
        if self.tasks:
            self.currentTask = self.tasks.pop(0)
            LogNotice("Starting task %@ with %d phases",
                      self.currentTask["title"],
                      len(self.currentTask["phases"]))
            self.nextPhase()
            LogDebug("Calling %@()", self.currentTask["title"])
            self.currentTask["method"]()
            LogDebug("Returned from %@()", self.currentTask["title"])
        else:
            LogNotice("Build finished successfully, image saved to %@",
                      self.outputPath())
            self.delegate.buildSucceeded()
            self.stop()
コード例 #3
0
    def launchScript_(self, args):
        LogDebug("launchScript:")

        if (self.authPassword() is None) and (os.getuid() != 0):
            # Use GUI dialog to elevate privileges.
            task = STPrivilegedTask.alloc().init()
            task.setLaunchPath_(args[0])
            task.setArguments_(args[1:])
            status = task.launch()
            LogNotice("Install task launched with return code: %d", status)
            if status:
                self.performSelectorOnMainThread_withObject_waitUntilDone_(
                    self.handleLaunchScriptError_, status, False)
        else:
            task = NSTask.alloc().init()
            if os.getuid() == 0:
                # No privilege elevation necessary.
                task.setLaunchPath_(args[0])
                task.setArguments_(args[1:])
            else:
                # Use sudo to elevate privileges.
                task.setLaunchPath_("/usr/bin/sudo")
                task.setArguments_(["-kSE"] + args)
                # Send password to sudo on stdin.
                passwordpipe = NSPipe.alloc().init()
                task.setStandardInput_(passwordpipe.fileHandleForReading())
                task.setStandardOutput_(
                    NSFileHandle.fileHandleWithNullDevice())
                task.setStandardError_(NSFileHandle.fileHandleWithNullDevice())
                writer = passwordpipe.fileHandleForWriting()
                pwd = NSString.stringWithString_(self.authPassword() + "\n")
                writer.writeData_(pwd.dataUsingEncoding_(NSUTF8StringEncoding))
                writer.closeFile()
            try:
                task.launch()
                LogNotice("Install task launched%@",
                          " with sudo" if os.getuid() != 0 else "")
            except BaseException as e:
                LogWarning("Install task launch failed with exception")
                self.performSelectorOnMainThread_withObject_waitUntilDone_(
                    self.handleLaunchScriptError_, str(e), False)
                return
            task.waitUntilExit()
            LogNotice("Install task finished with exit status %d",
                      task.terminationStatus())
            if task.terminationStatus() != 0:
                self.performSelectorOnMainThread_withObject_waitUntilDone_(
                    self.handleLaunchScriptError_,
                    "Install task failed with status %d" %
                    task.terminationStatus(), False)
コード例 #4
0
ファイル: IEDCLIController.py プロジェクト: ss-pq/AutoDMG
 def sourceSucceeded_(self, info):
     self.installerName = info[u"name"]
     self.installerVersion = info[u"version"]
     self.installerBuild = info[u"build"]
     LogNotice(u"Found installer: %s %s %s" %
               (info[u"name"], info[u"version"], info[u"build"]))
     self.busy = False
コード例 #5
0
    def taskFinalize(self):
        LogNotice(u"Finalize task running")

        self.delegate.buildSetProgressMessage_(
            u"Scanning disk image for restore")
        # The script is wrapped with progresswatcher.py which parses script
        # output and sends it back as notifications to IEDSocketListener.
        args = [
            NSBundle.mainBundle().pathForResource_ofType_(
                u"progresswatcher", u"py"),
            u"--socket",
            self.listenerPath,
            u"imagescan",
            self.outputPath(),
        ]
        LogInfo(u"Launching finalize with arguments:")
        for arg in args:
            LogInfo(u"    '%@'", arg)
        try:
            p = subprocess.Popen(args,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
            out = p.communicate()[0].decode(u"utf-8")
            LogDebug(u"Finalize exited with status %d and output '%@'",
                     p.returncode, out)
            if p.returncode != 0:
                errMsg = u"Finalize task failed with status %d" % p.returncode
                LogError(u"%@: %@", errMsg, out)
                self.fail_details_(errMsg, out)
        except BaseException as e:
            LogError(u"Failed to launch finalize task: %@", unicode(e))
            self.fail_details_(u"Failed to launch finalize task", unicode(e))
コード例 #6
0
    def taskInstall(self):
        LogNotice("Install task running")

        # The script is wrapped with progresswatcher.py which parses script
        # output and sends it back as notifications to IEDSocketListener.
        args = [
            NSBundle.mainBundle().pathForResource_ofType_(
                "progresswatcher", "py"),
            "--cd",
            NSBundle.mainBundle().resourcePath(),
            "--socket",
            self.listenerPath,
            "installesdtodmg",
            "--user",
            str(os.getuid()),
            "--group",
            str(os.getgid()),
            "--fstype",
            self.filesystem(),
            "--output",
            self.outputPath(),
            "--volume-name",
            self.volumeName(),
            "--size",
            str(self.volumeSize()),
            "--template",
            self.templatePath,
        ]
        if self.sourceType == IEDWorkflow.SYSTEM_IMAGE:
            args.extend(["--baseimage", self.source()])
        args.extend(self.packagesToInstall)
        LogInfo("Launching install with arguments:")
        for arg in args:
            LogInfo("    '%@'", arg)
        self.performSelectorInBackground_withObject_(self.launchScript_, args)
コード例 #7
0
 def connectionDidFinishLoading_(self, connection):
     LogInfo(u"%@ finished downloading to %@", self.package.name(),
             self.cacheTmpPath_(self.package.sha1()))
     self.fileHandle.closeFile()
     self.delegate.downloadStopped_(self.package)
     if self.checksum.hexdigest() == self.package.sha1():
         try:
             os.rename(self.cacheTmpPath_(self.package.sha1()),
                       self.cachePath_(self.package.sha1()))
         except OSError as e:
             error = u"Failed when moving download to %s: %s" % (
                 self.cachePath_(self.package.sha1()), unicode(e))
             LogError(error)
             self.delegate.downloadFailed_withError_(self.package, error)
             return
         linkPath = self.updatePath_(self.package.sha1())
         try:
             os.symlink(self.package.sha1(), linkPath)
         except OSError as e:
             error = u"Failed when creating link from %s to %s: %s" % (
                 self.package.sha1(), linkPath, unicode(e))
             LogError(error)
             self.delegate.downloadFailed_withError_(self.package, error)
             return
         LogNotice(u"%@ added to cache with sha1 %@", self.package.name(),
                   self.package.sha1())
         self.delegate.downloadSucceeded_(self.package)
         self.downloadNextUpdate()
     else:
         error = u"Expected sha1 checksum %s but got %s" % (
             sha1.lower(), m.hexdigest().lower())
         LogError(error)
         self.delegate.downloadFailed_withError_(self.package, error)
コード例 #8
0
    def cmdDownload_(self, args):
        """Download updates"""

        profile = self.profileController.profileForVersion_Build_(
            args.version, args.build)
        if profile is None:
            self.failWithMessage_(
                self.profileController.whyNoProfileForVersion_build_(
                    args.version, args.build))
            return os.EX_DATAERR

        updates = list()
        for update in profile:
            if not self.cache.isCached_(update["sha1"]):
                package = IEDPackage.alloc().init()
                package.setName_(update["name"])
                package.setPath_(self.cache.updatePath_(update["sha1"]))
                package.setSize_(update["size"])
                package.setUrl_(update["url"])
                package.setSha1_(update["sha1"])
                updates.append(package)

        if updates:
            self.cache.downloadUpdates_(updates)
            self.busy = True
            self.waitBusy()

        if self.hasFailed:
            return 1  # EXIT_FAILURE

        LogNotice("All updates for %@ %@ downloaded", args.version, args.build)

        return os.EX_OK
コード例 #9
0
 def sourceSucceeded_(self, info):
     self.installerName = info["name"]
     self.installerVersion = info["version"]
     self.installerBuild = info["build"]
     LogNotice("Found installer: %@ %@ %@", info["name"], info["version"],
               info["build"])
     self.busy = False
コード例 #10
0
ファイル: IEDCLIController.py プロジェクト: dmpfarid/AutoDMG
 def cmdList_(self, args):
     """List updates"""
     
     profile = self.profileController.profileForVersion_Build_(args.version, args.build)
     if profile is None:
         self.failWithMessage_(self.profileController.whyNoProfileForVersion_build_(args.version, args.build))
         return os.EX_DATAERR
     
     LogNotice(u"%d update%@ for %@ %@:", len(profile), u"" if len(profile) == 1 else u"s", args.version, args.build)
     for update in profile:
         LogNotice(u"    %@%@ (%@)",
                   u"[cached] " if self.cache.isCached_(update[u"sha1"]) else u"",
                   update[u"name"],
                   IEDUtil.formatBytes_(update[u"size"]))
     
     return os.EX_OK
コード例 #11
0
    def socketReceivedMessage_(self, msg):
        # The message is a dictionary with "action" as the only required key.
        action = msg["action"]

        if action == "update_progress":
            percent = msg["percent"]
            currentProgress = self.progress + self.currentPhase[
                "weight"] * percent / 100.0
            self.delegate.buildSetProgress_(currentProgress)

        elif action == "update_message":
            if self.lastUpdateMessage != msg["message"]:
                # Only log update messages when they change.
                LogInfo("%@", msg["message"])
            self.lastUpdateMessage = msg["message"]
            self.delegate.buildSetProgressMessage_(msg["message"])

        elif action == "select_phase":
            LogNotice("Script phase: %@", msg["phase"])
            self.nextPhase()

        elif action == "log_message":
            LogMessage(msg["log_level"], msg["message"])

        elif action == "notify_failure":
            self.fail_details_("Build failed", msg["message"])

        elif action == "notify_success":
            LogNotice("Build success: %@", msg["message"])

        elif action == "task_done":
            status = msg["termination_status"]
            if status == 0:
                self.nextTask()
            else:
                details = NSString.stringWithFormat_(
                    "Task exited with status %@", msg["termination_status"])
                LogError("%@", details)
                # Status codes 100-199 are from installesdtodmg.sh, and have
                # been preceeded by a "notify_failure" message.
                if (status < 100) or (status > 199):
                    self.fail_details_("Build failed", details)

        else:
            self.fail_details_("Unknown progress notification", "Message: %@",
                               msg)
コード例 #12
0
    def profileForVersion_Build_(self, version, build):
        """Return the update profile for a certain OS X version and build."""

        try:
            profile = self.profiles["%s-%s" % (version, build)]
            LogInfo("Update profile for %@ %@: %@", version, build,
                    ", ".join(u["name"] for u in profile))
        except KeyError:
            profile = None
            LogNotice("No update profile for %@ %@", version, build)
        return profile
コード例 #13
0
    def checkVersion_(self, mountPoint):
        LogDebug("checkVersion:%@", mountPoint)

        # We're now examining InstallESD.dmg for 10.7/10.8, BaseSystem.dmg for
        # 10.9, or a system image.
        name, version, build = IEDUtil.readSystemVersion_(mountPoint)
        if self.baseSystemMountedFromPath:
            self.dmgHelper.detach_selector_(self.baseSystemMountedFromPath,
                                            self.handleDetachResult_)
        installerVersion = IEDUtil.splitVersion(version)
        runningVersion = IEDUtil.hostVersionTuple()
        LogNotice("Found source: %@ %@ %@", name, version, build)
        if installerVersion[:2] != runningVersion[:2]:
            self.delegate.ejectingSource()
            majorVersion = ".".join(str(x) for x in installerVersion[:2])
            self.delegate.sourceFailed_text_("OS version mismatch",
                                             "The major version of the installer and the current OS must match.\n\n" + \
                                             "%s %s %s installer requires running %s.x to build an image." % (
                                                name, version, build,
                                                majorVersion))
            self.dmgHelper.detachAll_(self.alertFailedUnmounts_)
            return
        LogNotice("Accepted source %@: %@ %@ %@", self.newSourcePath, name,
                  version, build)
        self._source = self.newSourcePath
        self.installerName = name
        self.installerVersion = version
        self.installerBuild = build
        info = {
            "name": name,
            "version": version,
            "build": build,
            "template": self.loadImageTemplate_(mountPoint),
            "sourceType": self.sourceType,
        }
        self.delegate.sourceSucceeded_(info)
        # There's no reason to keep the dmg mounted if it's not an InstallESD.
        if self.sourceType != IEDWorkflow.INSTALL_ESD:
            self.dmgHelper.detachAll_(self.ejectSystemImage_)
コード例 #14
0
ファイル: IEDWorkflow.py プロジェクト: cev2107/AutoDMG
 def taskFinalize(self):
     LogNotice("Finalize task running")
     
     self.delegate.buildSetProgressMessage_("Scanning disk image for restore")
     # The script is wrapped with progresswatcher.py which parses script
     # output and sends it back as notifications to IEDSocketListener.
     args = [
         NSBundle.mainBundle().pathForResource_ofType_("progresswatcher", "py"),
         "--socket", self.listenerPath,
         "imagescan",
         self.outputPath(),
     ]
     LogInfo("Launching finalize with arguments:")
     for arg in args:
         LogInfo("    '%@'", arg)
     self.performSelectorInBackground_withObject_(self.launchFinalize_, args)
コード例 #15
0
 def taskFinalize(self):
     LogNotice(u"Finalize task running")
     
     self.delegate.buildSetProgressMessage_(u"Scanning disk image for restore")
     # The script is wrapped with progresswatcher.py which parses script
     # output and sends it back as notifications to IEDSocketListener.
     args = [
         NSBundle.mainBundle().pathForResource_ofType_(u"progresswatcher", u"py"),
         u"--socket", self.listenerPath,
         u"imagescan",
         self.outputPath(),
     ]
     LogInfo(u"Launching finalize with arguments:")
     for arg in args:
         LogInfo(u"    '%@'", arg)
     subprocess.Popen(args)
コード例 #16
0
    def nextPhase(self):
        LogDebug("nextPhase, currentPhase == %@", self.currentPhase)

        if self.currentPhase:
            self.progress += self.currentPhase["weight"]
            LogInfo("Phase %@ with weight %ld finished after %.3f seconds",
                    self.currentPhase["title"], self.currentPhase["weight"],
                    time.time() - self.phaseStartTime)
        self.phaseStartTime = time.time()
        try:
            self.currentPhase = self.currentTask["phases"].pop(0)
        except IndexError:
            self.fail_details_("No phase left in task",
                               traceback.format_stack())
            return
        LogNotice("Starting phase: %@", self.currentPhase["title"])
        self.delegate.buildSetPhase_(self.currentPhase["title"])
        self.delegate.buildSetProgress_(self.progress)
コード例 #17
0
 def connectionDidFinishLoading_(self, connection):
     LogDebug("Downloaded profile with %d bytes",
              self.profileUpdateData.length())
     if self.profileUpdateWindow:
         # Hide the progress window.
         self.profileUpdateWindow.orderOut_(self)
     # Decode the plist.
     plist, format, error = NSPropertyListSerialization.propertyListWithData_options_format_error_(
         self.profileUpdateData, NSPropertyListImmutable, None, None)
     if not plist:
         self.delegate.profileUpdateFailed_(error)
         return
     LogNotice("Downloaded update profiles with PublicationDate %@",
               plist["PublicationDate"])
     # Update the user's profiles if it's newer.
     latestProfiles = self.updateUsersProfilesIfNewer_(plist)
     # Load the latest profiles.
     self.loadProfilesFromPlist_(latestProfiles)
     # Notify delegate.
     self.delegate.profileUpdateSucceeded_(
         latestProfiles["PublicationDate"])
     self.delegate.profileUpdateAllDone()
コード例 #18
0
    def cmdBuild_(self, args):
        """Build image"""

        # Parse arguments.

        sourcePath = IEDUtil.installESDPath_(args.source) or \
                        IEDUtil.systemImagePath_(args.source)
        if sourcePath:
            templatePath = None
        else:
            templatePath = self.checkTemplate_(args.source)

        if not sourcePath and not templatePath:
            self.failWithMessage_(
                "'%s' is not a valid OS X installer, OS X system image or AutoDMG template"
                % args.source)
            return os.EX_DATAERR

        if templatePath:
            template = IEDTemplate.alloc().init()
            error = template.loadTemplateAndReturnError_(templatePath)
            if error:
                self.failWithMessage_("Couldn't load template from '%s': %s" %
                                      (templatePath, error))
                return os.EX_DATAERR
        else:
            template = IEDTemplate.alloc().initWithSourcePath_(sourcePath)

        if args.installer:
            template.setSourcePath_(args.installer)
        if args.output:
            template.setOutputPath_(args.output)
        if args.name:
            template.setVolumeName_(args.name)
        if args.size:
            template.setVolumeSize_(args.size)
        if args.skip_asr_imagescan:
            template.setFinalizeAsrImagescan_(False)
        if args.filesystem:
            template.setFilesystem_(args.filesystem)
        if args.updates is not None:
            template.setApplyUpdates_(True)
        if args.packages:
            if not template.setAdditionalPackages_(args.packages):
                self.failWithMessage_(
                    "Additional packages failed verification: %s" %
                    template.additionalPackageError)
                return os.EX_DATAERR

        if not template.sourcePath:
            self.failWithMessage_("No source path")
            return os.EX_USAGE
        if not template.outputPath:
            self.failWithMessage_("No output path")
            return os.EX_USAGE

        LogNotice("Installer: %@", template.sourcePath)

        # Set the source.
        self.busy = True
        self.workflow.setSource_(template.sourcePath)
        self.waitBusy()
        if self.hasFailed:
            return os.EX_DATAERR

        template.resolveVariables_({
            "OSNAME": self.installerName,
            "OSVERSION": self.installerVersion,
            "OSBUILD": self.installerBuild,
        })

        LogNotice("Output Path: %@", template.outputPath)
        LogNotice("Volume Name: %@", template.volumeName)

        # Generate the list of updates to install.
        updates = list()
        if template.applyUpdates:
            profile = self.profileController.profileForVersion_Build_(
                self.installerVersion, self.installerBuild)
            if profile is None:
                self.failWithMessage_(
                    self.profileController.whyNoProfileForVersion_build_(
                        self.installerVersion, self.installerBuild))
                return os.EX_DATAERR

            missingUpdates = list()

            for update in profile:
                LogNotice("Update: %@ (%@)", update["name"],
                          IEDUtil.formatByteSize_(update["size"]))
                package = IEDPackage.alloc().init()
                package.setName_(update["name"])
                package.setPath_(self.cache.updatePath_(update["sha1"]))
                package.setSize_(update["size"])
                package.setUrl_(update["url"])
                package.setSha1_(update["sha1"])
                if not self.cache.isCached_(update["sha1"]):
                    if args.download_updates:
                        missingUpdates.append(package)
                    else:
                        self.failWithMessage_(
                            "Can't apply updates, %s is missing from cache" %
                            update["name"])
                        return os.EX_DATAERR
                updates.append(package)

            if missingUpdates:
                self.cache.downloadUpdates_(missingUpdates)
                self.busy = True
                self.waitBusy()
                if self.hasFailed:
                    self.failWithMessage_(
                        "Can't build due to updates missing from cache")
                    return 1  # EXIT_FAILURE
                updates.extend(missingUpdates)
                LogNotice("All updates for %@ %@ downloaded",
                          self.installerVersion, self.installerBuild)

        # Generate the list of additional packages to install.
        template.resolvePackages()
        for package in template.packagesToInstall:
            LogNotice("Package: %@ (%@)", package.name(),
                      IEDUtil.formatByteSize_(package.size()))

        # Check the output path.
        if os.path.exists(template.outputPath):
            if args.force:
                try:
                    os.unlink(template.outputPath)
                except OSError as e:
                    self.failWithMessage_("Couldn't remove %s: %s" %
                                          (template.outputPath, str(e)))
                    return os.EX_CANTCREAT
            else:
                self.failWithMessage_("%s already exists" %
                                      template.outputPath)
                return os.EX_CANTCREAT
        else:
            outputDir = os.path.dirname(template.outputPath)
            if outputDir and not os.path.exists(outputDir):
                try:
                    os.makedirs(outputDir)
                except OSError as e:
                    self.failWithMessage_(
                        "%s does not exist and can't be created: %s" %
                        (outputDir, str(e)))
                    return os.EX_CANTCREAT

        # If we're not running as root get the password for authentication.
        if os.getuid() != 0:
            username = NSUserName()
            currentUser = CBIdentity.identityWithName_authority_(
                username, CBIdentityAuthority.defaultIdentityAuthority())
            passwordOK = False
            while not passwordOK:
                password = getpass.getpass("Password for %s: " %
                                           username).decode("utf-8")
                if currentUser.authenticateWithPassword_(password):
                    passwordOK = True
            self.workflow.setAuthUsername_(username)
            self.workflow.setAuthPassword_(password)

        # Start the workflow.
        self.busy = True
        self.workflow.setPackagesToInstall_(updates +
                                            template.packagesToInstall)
        self.workflow.setOutputPath_(template.outputPath)
        self.workflow.setVolumeName_(template.volumeName)
        self.workflow.setVolumeSize_(template.volumeSize)
        self.workflow.setFinalizeAsrImagescan_(template.finalizeAsrImagescan)
        self.workflow.setFilesystem_(template.filesystem)
        self.workflow.setTemplate_(template)
        self.workflow.start()
        self.waitBusy()
        if self.hasFailed:
            return 1  # EXIT_FAILURE

        return os.EX_OK
コード例 #19
0
 def downloadStarting_(self, package):
     LogNotice("Downloading %@ (%@)", package.name(),
               IEDUtil.formatByteSize_(package.size()))
     self.lastProgressPercent = -100.0
     self.lastProgressTimestamp = NSDate.alloc().init()
コード例 #20
0
 def buildSucceeded(self):
     LogNotice("Build successful")
コード例 #21
0
 def buildSetPhase_(self, phase):
     LogNotice("phase: %@", phase)
コード例 #22
0
    def start(self):
        LogNotice("Starting build")
        LogNotice("Using installer: %@ %@ %@", self.installerName,
                  self.installerVersion, self.installerBuild)
        LogNotice("Using output path: %@", self.outputPath())
        LogNotice("TMPDIR is set to: %@", os.getenv("TMPDIR"))
        self.delegate.buildStartingWithOutput_(self.outputPath())

        self.createTempDir()
        LogDebug("Created temporary directory at %@", self.tempDir)

        if not self.template():
            self.fail_details_(
                "Template missing",
                "A template for inclusion in the image is required.")
            return

        datestamp = datetime.datetime.today().strftime("%Y%m%d")
        self.templatePath = os.path.join(self.tempDir,
                                         "AutoDMG-%s.adtmpl" % datestamp)
        LogDebug("Saving template to %@", self.templatePath)
        error = self.template().saveTemplateAndReturnError_(self.templatePath)
        if error:
            self.fail_details_("Couldn't save template to tempdir", error)
            return

        # The workflow is split into tasks, and each task has one or more
        # phases. Each phase of the installation is given a weight for the
        # progress bar, calculated from the size of the installer package.
        # Phases that don't install packages get an estimated weight.

        self.tasks = list()

        # Prepare for install.
        self.tasks.append({
            "title":
            "Prepare",
            "method":
            self.taskPrepare,
            "phases": [
                {
                    "title": "Preparing",
                    "weight": 34 * 1024 * 1024
                },
            ],
        })

        # Perform installation.
        installerPhases = [
            {
                "title": "Starting install",
                "weight": 21 * 1024 * 1024
            },
            {
                "title": "Creating disk image",
                "weight": 21 * 1024 * 1024
            },
        ]
        if self.sourceType != IEDWorkflow.SYSTEM_IMAGE:
            installerPhases.append({
                "title": "Installing OS",
                "weight": 4 * 1024 * 1024 * 1024,
            })
        for package in self.additionalPackages:
            installerPhases.append({
                "title":
                "Installing %s" % package.name(),
                # Add 100 MB to the weight to account for overhead.
                "weight":
                package.size() + 100 * 1024 * 1024,
            })
        installerPhases.extend([
            # hdiutil convert.
            {
                "title": "Converting disk image",
                "weight": 313 * 1024 * 1024
            },
        ])
        self.tasks.append({
            "title": "Install",
            "method": self.taskInstall,
            "phases": installerPhases,
        })

        # Finalize image. (Skip adding this task if Finalize: Scan for restore is unchecked.)
        if self._finalizeAsrImagescan:
            self.tasks.append({
                "title":
                "Finalize",
                "method":
                self.taskFinalize,
                "phases": [
                    {
                        "title": "Scanning disk image",
                        "weight": 2 * 1024 * 1024
                    },
                    {
                        "title": "Scanning disk image",
                        "weight": 1 * 1024 * 1024
                    },
                    {
                        "title": "Scanning disk image",
                        "weight": 150 * 1024 * 1024
                    },
                    {
                        "title": "Scanning disk image",
                        "weight": 17 * 1024 * 1024,
                        "optional": True
                    },
                ],
            })

        # Finish build.
        self.tasks.append({
            "title":
            "Finish",
            "method":
            self.taskFinish,
            "phases": [
                {
                    "title": "Finishing",
                    "weight": 1 * 1024 * 1024
                },
            ],
        })

        # Calculate total weight of all phases.
        self.totalWeight = 0
        for task in self.tasks:
            LogInfo("Task %@ with %d phases:", task["method"].__name__,
                    len(task["phases"]))
            for phase in task["phases"]:
                LogInfo("    Phase '%@' with weight %.1f", phase["title"],
                        phase["weight"] / 1048576.0)
                self.totalWeight += phase["weight"]
        self.delegate.buildSetTotalWeight_(self.totalWeight)

        # Start the first task.
        self.progress = 0
        self.currentTask = None
        self.currentPhase = None
        self.nextTask()
コード例 #23
0
    def connectionDidFinishLoading_(self, connection):
        LogDebug("Downloaded version check data with %d bytes",
                 self.plistData.length())
        # Decode the plist.
        plist, format, error = NSPropertyListSerialization.propertyListWithData_options_format_error_(
            self.plistData, NSPropertyListImmutable, None, None)
        if not plist:
            self.logFailure_(error.localizedDescription())
            return

        # Save the time stamp.
        self.defaults.setObject_forKey_(NSDate.date(), "LastAppVersionCheck")

        # Get latest version and build.
        latestDisplayVersion = plist["Version"]
        if latestDisplayVersion.count(".") == 1:
            latestPaddedVersion = latestDisplayVersion + ".0"
        else:
            latestPaddedVersion = latestDisplayVersion
        latestBuild = plist["Build"]
        latestVersionBuild = "%s.%s" % (latestPaddedVersion, latestBuild)
        LogNotice("Latest published version is AutoDMG v%@ build %@",
                  latestDisplayVersion, latestBuild)

        if self.checkSilently:
            # Check if we've already notified the user about this version.
            if latestVersionBuild == self.defaults.stringForKey_(
                    "NotifiedAppVersion"):
                LogDebug("User has already been notified of this version.")
                return

        # Convert latest version into a tuple with (major, minor, rev, build).
        latestTuple = IEDUtil.splitVersion(latestVersionBuild, strip="ab")

        # Get the current version and convert it to a tuple.
        displayVersion, build = IEDUtil.getAppVersion()
        if displayVersion.count(".") == 1:
            paddedVersion = displayVersion + ".0"
        else:
            paddedVersion = displayVersion
        versionBuild = "%s.%s" % (paddedVersion, build)
        currentTuple = IEDUtil.splitVersion(versionBuild, strip="ab")

        # Compare and notify
        if latestTuple > currentTuple:
            alert = NSAlert.alloc().init()
            alert.setMessageText_("A new version of AutoDMG is available")
            alert.setInformativeText_(
                "AutoDMG v%s build %s is available for download." %
                (latestDisplayVersion, latestBuild))
            alert.addButtonWithTitle_("Download")
            alert.addButtonWithTitle_("Skip")
            alert.addButtonWithTitle_("Later")
            button = alert.runModal()
            if button == NSAlertFirstButtonReturn:
                url = NSURL.URLWithString_(plist["URL"])
                NSWorkspace.sharedWorkspace().openURL_(url)
            elif button == NSAlertSecondButtonReturn:
                self.defaults.setObject_forKey_(latestVersionBuild,
                                                "NotifiedAppVersion")
        elif not self.checkSilently:
            alert = NSAlert.alloc().init()
            alert.setMessageText_("AutoDMG is up to date")
            if currentTuple > latestTuple:
                verString = "bleeding edge"
            else:
                verString = "current"
            alert.setInformativeText_(
                "AutoDMG v%s build %s appears to be %s." %
                (displayVersion, build, verString))
            alert.runModal()
コード例 #24
0
 def taskFinish(self):
     LogNotice("Finish")
     self.delegate.buildSetProgress_(self.totalWeight)
     self.nextTask()