From 29ab885a25a280da907e36525df985a793e10fa4 Mon Sep 17 00:00:00 2001 From: Elliot Jordan Date: Sun, 3 Mar 2019 08:40:09 -0800 Subject: [PATCH 1/4] Adjusted punctuation --- pre_commit_hooks/check_autopkg_recipes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pre_commit_hooks/check_autopkg_recipes.py b/pre_commit_hooks/check_autopkg_recipes.py index 11a6290..725dd7c 100755 --- a/pre_commit_hooks/check_autopkg_recipes.py +++ b/pre_commit_hooks/check_autopkg_recipes.py @@ -55,7 +55,7 @@ def main(argv=None): override_prefix = args.override_prefix if not recipe.get("Identifier", "").startswith(override_prefix): print( - '{}: override identifier does not start with "{}."'.format( + '{}: override identifier does not start with "{}"'.format( filename, override_prefix ) ) @@ -64,7 +64,7 @@ def main(argv=None): recipe_prefix = args.recipe_prefix if not recipe.get("Identifier", "").startswith(recipe_prefix): print( - '{}: recipe identifier does not start with "{}."'.format( + '{}: recipe identifier does not start with "{}"'.format( filename, recipe_prefix ) ) From b1b97f31ccc3f67667a5e557ecc6aa60d26c0398 Mon Sep 17 00:00:00 2001 From: Elliot Jordan Date: Mon, 4 Mar 2019 16:34:02 -0800 Subject: [PATCH 2/4] Fixed required keys example, use name and description by default --- README.md | 4 ++-- pre_commit_hooks/check_munki_pkgsinfo.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2ec618d..f5d9a94 100644 --- a/README.md +++ b/README.md @@ -64,9 +64,9 @@ For any hook in this repo you wish to use, add the following to your pre-commit This hook checks Munki pkginfo files to ensure they are valid. - Specify your preferred list of pkginfo categories, if you wish to enforce it: - `['--categories=Productivity,Design,Utilities']` + `[--categories, Productivity, Design, Utilities, 'Web Browsers']` - Specify required pkginfo keys: - `['--required-keys=category,description,developer,name,version']` (default: category, description, developer, name) + `[--required-keys, category, description, developer, name, version]` (default: description, name) - __check-munkiadmin-scripts__ diff --git a/pre_commit_hooks/check_munki_pkgsinfo.py b/pre_commit_hooks/check_munki_pkgsinfo.py index d264b44..60b506e 100755 --- a/pre_commit_hooks/check_munki_pkgsinfo.py +++ b/pre_commit_hooks/check_munki_pkgsinfo.py @@ -18,7 +18,7 @@ def build_argument_parser(): parser.add_argument( "--required-keys", nargs="+", - default=["category", "description", "developer", "name"], + default=["description", "name"], help="List of required pkginfo keys.", ) return parser From 99fb86c68f83a420a1917db07eb109832ca729dd Mon Sep 17 00:00:00 2001 From: Elliot Jordan Date: Wed, 13 Mar 2019 15:58:26 -0700 Subject: [PATCH 3/4] Disable plist top level dict checking for now --- pre_commit_hooks/check_plists.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pre_commit_hooks/check_plists.py b/pre_commit_hooks/check_plists.py index 1b6ab85..399140f 100755 --- a/pre_commit_hooks/check_plists.py +++ b/pre_commit_hooks/check_plists.py @@ -27,9 +27,10 @@ def main(argv=None): for filename in args.filenames: try: plist = plistlib.readPlist(filename) - if not isinstance(plist, dict): - print("{}: top level of plist should be type dict".format(filename)) - retval = 1 + # Possible future addition, but disabled for now. + # if not isinstance(plist, dict): + # print("{}: top level of plist should be type dict".format(filename)) + # retval = 1 except (ExpatError, ValueError) as err: print("{}: plist parsing error: {}".format(filename, err)) retval = 1 From 0f45dd8865ffcf511e1f7797ac313c9ed4a92b9b Mon Sep 17 00:00:00 2001 From: Elliot Jordan Date: Wed, 13 Mar 2019 15:58:47 -0700 Subject: [PATCH 4/4] Enable basic type checking for pkginfo dicts --- pre_commit_hooks/check_autopkg_recipes.py | 6 ++ pre_commit_hooks/check_munki_pkgsinfo.py | 5 ++ pre_commit_hooks/util.py | 83 +++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 pre_commit_hooks/util.py diff --git a/pre_commit_hooks/check_autopkg_recipes.py b/pre_commit_hooks/check_autopkg_recipes.py index 725dd7c..18f77f0 100755 --- a/pre_commit_hooks/check_autopkg_recipes.py +++ b/pre_commit_hooks/check_autopkg_recipes.py @@ -6,6 +6,8 @@ import plistlib from xml.parsers.expat import ExpatError +from pre_commit_hooks.util import validate_pkginfo_key_types + def build_argument_parser(): """Build and return the argument parser.""" @@ -70,6 +72,10 @@ def main(argv=None): ) retval = 1 + input = recipe.get("Input", recipe.get("input", recipe.get("INPUT"))) + if input and "pkginfo" in input: + retval = validate_pkginfo_key_types(input["pkginfo"], filename) + return retval diff --git a/pre_commit_hooks/check_munki_pkgsinfo.py b/pre_commit_hooks/check_munki_pkgsinfo.py index 60b506e..8e53539 100755 --- a/pre_commit_hooks/check_munki_pkgsinfo.py +++ b/pre_commit_hooks/check_munki_pkgsinfo.py @@ -6,6 +6,8 @@ import plistlib from xml.parsers.expat import ExpatError +from pre_commit_hooks.util import validate_pkginfo_key_types + def build_argument_parser(): """Build and return the argument parser.""" @@ -54,6 +56,9 @@ def main(argv=None): retval = 1 break # No need to continue checking this file + # Ensure pkginfo keys have expected types. + retval = validate_pkginfo_key_types(pkginfo, filename) + # Check for rogue categories. if args.categories and pkginfo.get("category") not in args.categories: print( diff --git a/pre_commit_hooks/util.py b/pre_commit_hooks/util.py new file mode 100644 index 0000000..ab60d4a --- /dev/null +++ b/pre_commit_hooks/util.py @@ -0,0 +1,83 @@ +#!/usr/bin/python + +from datetime import datetime + + +def validate_pkginfo_key_types(pkginfo, filename): + """Main process.""" + + # Pkginfo keys and their known types. Omitted keys are left unvalidated. + # Source: https://github.com/munki/munki/wiki/Supported-Pkginfo-Keys + # Last updated 2019-03-13. + pkginfo_types = { + "additional_startosinstall_options": list, + "apple_item": bool, + "autoremove": bool, + "blocking_applications": list, + "catalogs": list, + "category": str, + "copy_local": bool, + "description": str, + "developer": str, + "display_name": str, + "force_install_after_date": datetime, + "forced_install": bool, + "forced_uninstall": bool, + "icon_name": str, + "installable_condition": str, + "installed_size": int, + "installer_item_hash": str, + "installer_item_location": str, + "installer_item_size": int, + "installer_type": str, + "installs": list, + "items_to_copy": list, + "installer_choices_xml": list, + "installer_environment": dict, + "localized_strs": dict, + "minimum_munki_version": str, + "minimum_os_version": str, + "maximum_os_version": str, + "name": str, + "notes": str, + "PackageCompleteURL": str, + "PackageURL": str, + "package_path": str, + "installcheck_script": str, + "uninstallcheck_script": str, + "OnDemand": bool, + "postinstall_script": str, + "postuninstall_script": str, + "precache": bool, + "preinstall_alert": dict, + "preuninstall_alert": dict, + "preupgrade_alert": dict, + "preinstall_script": str, + "preuninstall_script": str, + "receipts": list, + "requires": list, + "RestartAction": str, + "supported_architectures": list, + "suppress_bundle_relocation": bool, + "unattended_install": bool, + "unattended_uninstall": bool, + "uninstall_method": str, + "uninstall_script": str, + "uninstaller_item_location": str, + "uninstallable": bool, + "update_for": list, + "version": str, + } + + retval = 0 + for pkginfo_key, expected_type in PKGINFO_TYPES.items(): + if pkginfo_key in pkginfo: + if not isinstance(pkginfo[pkginfo_key], expected_type): + print( + "{}: pkginfo key {} should be type {}, not type {}".format( + filename, pkginfo_key, expected_type, type(pkginfo[pkginfo_key]) + ) + ) + retval = 1 + + return retval