From 3970a72f7e68be945ab7a344a522283681568b6b Mon Sep 17 00:00:00 2001 From: Elliot Jordan Date: Thu, 4 Jul 2024 14:41:44 -0700 Subject: [PATCH] Switch QuickSilver.pkg back to stock AutoPkg processors Reverts https://github.com/autopkg/homebysix-recipes/pull/332. --- Quicksilver/FoundationDmgVersioner.py | 146 ------------- Quicksilver/FoundationPkgCreator.py | 303 -------------------------- Quicksilver/Quicksilver.pkg.recipe | 8 +- 3 files changed, 4 insertions(+), 453 deletions(-) delete mode 100644 Quicksilver/FoundationDmgVersioner.py delete mode 100644 Quicksilver/FoundationPkgCreator.py diff --git a/Quicksilver/FoundationDmgVersioner.py b/Quicksilver/FoundationDmgVersioner.py deleted file mode 100644 index 1d6f433b..00000000 --- a/Quicksilver/FoundationDmgVersioner.py +++ /dev/null @@ -1,146 +0,0 @@ -# -*- coding: utf-8 -*- -# pylint: disable=no-else-raise, invalid-name -# -# Based on AppDmgVersioner, Copyright 2010 Per Olofsson -# Adapted to use Foundation, 2020 Graham Pugh -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""See docstring for FoundationDmgVersioner class""" - -import glob -import os.path - -from autopkglib import ProcessorError # pylint: disable=import-error -from autopkglib.DmgMounter import DmgMounter # pylint: disable=import-error -from Foundation import ( # pylint: disable=E0611 - NSData, - NSPropertyListMutableContainers, - NSPropertyListSerialization, -) - -# Disable PyLint complaining about 'invalid' camelCase names -# pylint: disable=C0103 - - -__all__ = ["FoundationDmgVersioner"] - - -class FoundationDmgVersioner(DmgMounter): # pylint: disable=invalid-name - # we dynamically set the docstring from the description (DRY), so: - description = "Extracts bundle ID and version of app inside dmg." - input_variables = { - "dmg_path": { - "required": True, - "description": "Path to a dmg containing an app.", - } - } - output_variables = { - "app_name": { - "description": ( - "Name of app found at the root of the disk image. This does not search " - "recursively for a matching app. If you need to specify a path, use " - "Versioner instead." - ) - }, - "bundleid": {"description": "Bundle identifier of the app."}, - "version": {"description": "Version of the app."}, - } - - __doc__ = description - - def readPlist(self, filepath): - """ - Read a .plist file from filepath. Return the unpacked root object - (which is usually a dictionary). - """ - plistData = NSData.dataWithContentsOfFile_(filepath) - ( - dataObject, - dummy_plistFormat, - error, - ) = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_( - plistData, NSPropertyListMutableContainers, None, None - ) - if dataObject is None: - if error: - error = error.encode("ascii", "ignore") - else: - error = "Unknown error" - errmsg = "%s in file %s" % (error, filepath) - raise ProcessorError(errmsg) - else: - return dataObject - - def readPlistFromString(self, data): - """Read a plist data from a string. Return the root object.""" - try: - plistData = memoryview(data) - except TypeError as err: - raise ProcessorError(err) - ( - dataObject, - dummy_plistFormat, - error, - ) = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_( - plistData, NSPropertyListMutableContainers, None, None - ) - if dataObject is None: - if error: - error = error.encode("ascii", "ignore") - else: - error = "Unknown error" - raise ProcessorError(error) - else: - return dataObject - - def find_app(self, path): - """Find app bundle at path.""" - apps = glob.glob(os.path.join(path, "*.app")) - if len(apps) == 0: - raise ProcessorError("No app found in dmg") - return apps[0] - - def read_bundle_info(self, path): - """Read Contents/Info.plist inside a bundle.""" - - plistpath = os.path.join(path, "Contents", "Info.plist") - try: - info = self.readPlist(plistpath) - except Exception as error: - raise ProcessorError(f"Can't read {plistpath}: {error}") - return info - - def main(self): - """Main process.""" - # Mount the image. - mount_point = self.mount(self.env["dmg_path"]) - # Wrap all other actions in a try/finally so the image is always - # unmounted. - try: - app_path = self.find_app(mount_point) - info = self.read_bundle_info(app_path) - self.env["app_name"] = os.path.basename(app_path) - try: - self.env["bundleid"] = info["CFBundleIdentifier"] - self.env["version"] = info["CFBundleShortVersionString"] - self.output(f"BundleID: {self.env['bundleid']}") - self.output(f"Version: {self.env['version']}") - except BaseException as err: - raise ProcessorError(err) - finally: - self.unmount(self.env["dmg_path"]) - - -if __name__ == "__main__": - PROCESSOR = FoundationDmgVersioner() - PROCESSOR.execute_shell() diff --git a/Quicksilver/FoundationPkgCreator.py b/Quicksilver/FoundationPkgCreator.py deleted file mode 100644 index 8533fdaf..00000000 --- a/Quicksilver/FoundationPkgCreator.py +++ /dev/null @@ -1,303 +0,0 @@ -# -*- coding: utf-8 -*- -# pylint: disable=no-else-raise, invalid-name -# -# Based on AppPkgCreator, Copyright 2016 Greg Neagle -# Adapted to use Foundation, 2020 Graham Pugh -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""See docstring for FoundationPkgCreator class""" - -import os.path -import shutil -from glob import glob - -from autopkglib import ProcessorError # pylint: disable=import-error -from autopkglib.DmgMounter import DmgMounter # pylint: disable=import-error -from autopkglib.PkgCreator import PkgCreator # pylint: disable=import-error -from Foundation import ( # pylint: disable=E0611 - NSData, - NSPropertyListMutableContainers, - NSPropertyListSerialization, -) - -# Disable PyLint complaining about 'invalid' camelCase names -# pylint: disable=C0103 - - -__all__ = ["FoundationPkgCreator"] - - -class FoundationPkgCreator(DmgMounter, PkgCreator): # pylint: disable=invalid-name - """Calls autopkgserver to create a package from an application.""" - - description = __doc__ - input_variables = { - "app_path": { - "required": False, - "description": ( - "Path to an application to be packaged. Can be on a disk " - "image and globbed. If not set, defaults to %pathname%/*.app. " - "Typically %pathname% points to a disk image downloaded in a " - "prior recipe step." - ), - }, - "pkg_path": { - "required": False, - "description": "The pathname for the pkg to be created. If not set, " - "defaults to %RECIPE_CACHE_DIR%/%app_name%-%version%.pkg", - }, - "bundleid": { - "required": False, - "description": "Bundle identifier of the app. If not set, will be " - "extracted from the CFBundleIdentifier in the app's Info.plist.", - }, - "version": { - "required": False, - "description": "Version of the app. If not set, will be extracted from the " - "CFBundleShortVersionString in the app's Info.plist.", - }, - "force_pkg_build": { - "required": False, - "description": ( - "When set, this forces building a new package even if " - "a package already exists in the output directory with " - "the same identifier and version number. Defaults to False" - ), - }, - } - output_variables = { - "new_package_request": { - "description": "True if a new package was actually requested to be built. " - "False if a package with the same filename, identifier and " - "version already exists and thus no package was built (see " - "'force_pkg_build' input variable.)" - }, - "version": {"description": "Version of the app."}, - "app_pkg_creator_summary_result": { - "description": "Description of interesting results." - }, - } - - def readPlist(self, filepath): - """ - Read a .plist file from filepath. Return the unpacked root object - (which is usually a dictionary). - """ - plistData = NSData.dataWithContentsOfFile_(filepath) - ( - dataObject, - dummy_plistFormat, - error, - ) = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_( - plistData, NSPropertyListMutableContainers, None, None - ) - if dataObject is None: - if error: - error = error.encode("ascii", "ignore") - else: - error = "Unknown error" - errmsg = "%s in file %s" % (error, filepath) - raise ProcessorError(errmsg) - else: - return dataObject - - def readPlistFromString(self, data): - """Read a plist data from a string. Return the root object.""" - try: - plistData = memoryview(data) - except TypeError as err: - raise ProcessorError(err) - ( - dataObject, - dummy_plistFormat, - error, - ) = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_( - plistData, NSPropertyListMutableContainers, None, None - ) - if dataObject is None: - if error: - error = error.encode("ascii", "ignore") - else: - error = "Unknown error" - raise ProcessorError(error) - else: - return dataObject - - def read_info_plist(self, path): - """Read Contents/Info.plist from the app.""" - plistpath = os.path.join(path, "Contents", "Info.plist") - try: - info = self.readPlist(plistpath) - except Exception as err: - raise ProcessorError(f"Can't read {plistpath}: {err}") - return info - - def package_app(self, app_path): - """Build a packaging request, send it to the autopkgserver and get the - constructed package.""" - - # clear any pre-exising summary result - if "app_pkg_creator_summary_result" in self.env: - del self.env["app_pkg_creator_summary_result"] - - # get version and bundleid - infoplist = self.read_info_plist(app_path) - if not self.env.get("version"): - try: - self.env["version"] = infoplist["CFBundleShortVersionString"] - self.output(f"Version: {self.env['version']}") - except BaseException as err: - raise ProcessorError(err) - if not self.env.get("bundleid"): - try: - self.env["bundleid"] = infoplist["CFBundleIdentifier"] - self.output(f"BundleID: {self.env['bundleid']}") - except BaseException as err: - raise ProcessorError(err) - - # get pkgdir and pkgname - if self.env.get("pkg_path"): - pkg_path = self.env["pkg_path"] - pkgdir = os.path.dirname(pkg_path) - pkgname = os.path.splitext(os.path.basename(pkg_path))[0] - else: - pkgdir = self.env["RECIPE_CACHE_DIR"] - pkgname = ( - f"{os.path.splitext(os.path.basename(app_path))[0]}-" - f"{self.env['version']}" - ) - pkg_path = os.path.join(pkgdir, pkgname + ".pkg") - - # Check for an existing flat package in the output dir and compare - # its identifier and version to the one we're going to build. - if self.pkg_already_exists(pkg_path, self.env["bundleid"], self.env["version"]): - self.output( - "Existing package matches version and identifier, not building." - ) - self.env["pkg_path"] = pkg_path - self.env["new_package_request"] = False - return - - # create pkgroot and copy application into it - pkgroot = os.path.join(self.env["RECIPE_CACHE_DIR"], "payload") - if os.path.exists(pkgroot): - # remove it if it already exists - try: - if os.path.isdir(pkgroot) and not os.path.islink(pkgroot): - shutil.rmtree(pkgroot) - else: - os.unlink(pkgroot) - except OSError as err: - raise ProcessorError(f"Can't remove {pkgroot}: {err.strerror}") - try: - os.makedirs(os.path.join(pkgroot, "Applications"), 0o775) - except OSError as err: - raise ProcessorError(f"Could not create pkgroot: {err.strerror}") - - app_name = os.path.basename(app_path) - source_item = app_path - dest_item = os.path.join(pkgroot, "Applications", app_name) - try: - if os.path.isdir(source_item): - shutil.copytree(source_item, dest_item, symlinks=True) - elif not os.path.isdir(dest_item): - shutil.copyfile(source_item, dest_item) - else: - shutil.copy(source_item, dest_item) - self.output(f"Copied {source_item} to {dest_item}") - except OSError as err: - raise ProcessorError( - f"Can't copy {source_item} to {dest_item}: {err.strerror}" - ) - - # build a package request - request = { - "pkgroot": pkgroot, - "pkgdir": pkgdir, - "pkgname": pkgname, - "pkgtype": "flat", - "id": self.env["bundleid"], - "version": self.env["version"], - "infofile": "", - "resources": "", - "chown": [{"path": "Applications", "user": "root", "group": "admin"}], - "scripts": "", - } - - # Send packaging request. - try: - self.output("Connecting") - self.connect() - self.output("Sending packaging request") - self.env["new_package_request"] = True - pkg_path = self.send_request(request) - finally: - self.output("Disconnecting") - self.disconnect() - - # Return path to pkg. - self.env["pkg_path"] = pkg_path - self.env["app_pkg_creator_summary_result"] = { - "summary_text": "The following packages were built:", - "report_fields": ["identifier", "version", "pkg_path"], - "data": { - "identifier": request["id"], - "version": request["version"], - "pkg_path": pkg_path, - }, - } - - def main(self): - """Find an app, package it up""" - if self.env.get("app_path"): - app_path = self.env["app_path"] - elif self.env.get("pathname"): - app_path = self.env["pathname"] + "/*.app" - else: - raise ProcessorError("No app_path or pathname specified.") - # Check if we're trying to package something inside a dmg. - (dmg_path, dmg, dmg_app_path) = self.parsePathForDMG(app_path) - try: - if dmg: - # Mount dmg and return path inside. - mount_point = self.mount(dmg_path) - app_path = os.path.join(mount_point, dmg_app_path) - # process path with glob.glob - matches = glob(app_path) - if len(matches) == 0: - raise ProcessorError(f"Error processing path '{app_path}' with glob. ") - matched_app_path = matches[0] - if len(matches) > 1: - self.output( - f"WARNING: Multiple paths match 'app_path' glob '{app_path}':" - ) - for match in matches: - self.output(f" - {match}") - - if [c for c in "*?[]!" if c in app_path]: - self.output( - f"Using path '{matched_app_path}' matched from globbed " - f"'{app_path}'." - ) - - # do the copy - self.package_app(matched_app_path) - - finally: - if dmg: - self.unmount(dmg_path) - - -if __name__ == "__main__": - PROCESSOR = FoundationPkgCreator() - PROCESSOR.execute_shell() diff --git a/Quicksilver/Quicksilver.pkg.recipe b/Quicksilver/Quicksilver.pkg.recipe index 6c175cf9..0271b237 100644 --- a/Quicksilver/Quicksilver.pkg.recipe +++ b/Quicksilver/Quicksilver.pkg.recipe @@ -20,15 +20,15 @@ Arguments - dmg_path - %pathname% + input_plist_path + %pathname%/Quicksilver.app/Contents/Info.plist Processor - FoundationDmgVersioner + Versioner Processor - FoundationPkgCreator + AppPkgCreator