Skip to content

Instantly share code, notes, and snippets.

@homebysix
Created March 1, 2021 18:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save homebysix/9ac9f1fc0e48b36c1367f6ab1b561efe to your computer and use it in GitHub Desktop.
Save homebysix/9ac9f1fc0e48b36c1367f6ab1b561efe to your computer and use it in GitHub Desktop.
#!/usr/local/autopkg/python
import os
import plistlib
from glob import glob
from Foundation import CFPreferencesCopyAppValue
# Input keys we don't care about comparing.
EXCLUDED_KEYS = (
"MUNKI_CATEGORY",
"MUNKI_REPO_SUBDIR",
"NAME",
)
EXCLUDED_OVERRIDES = ()
def get_identifier(recipe):
"""Return identifier from recipe dict. Tries the Identifier
top-level key and falls back to the legacy key location.
Source: https://github.com/autopkg/autopkg/blob/8b8a008bdc5d4b309050394a84ebb505d10dc68c/Code/autopkglib/__init__.py#L281
"""
try:
return recipe["Identifier"]
except (KeyError, AttributeError):
try:
return recipe["Input"]["IDENTIFIER"]
except (KeyError, AttributeError):
return None
except TypeError:
return None
def get_recipe_index():
"""Return a tuple of tuples representing the recipe paths and their identifiers on this Mac."""
# Determine search paths for recipes.
recipe_search_dirs = CFPreferencesCopyAppValue(
"RECIPE_SEARCH_DIRS", "com.github.autopkg"
)
# Glob the search paths and add any found recipes to a recipe list.
recipes = []
for search_dir in recipe_search_dirs:
recipes.extend(glob(os.path.join(search_dir, "*.recipe")))
recipes.extend(glob(os.path.join(search_dir, "*/*.recipe")))
recipes = list(set(recipes))
# For each recipe in the list, determine an identifier and build an index.
recipe_index = {}
for recipe in recipes:
with open(recipe, "rb") as openfile:
recipe_data = plistlib.load(openfile)
recipe_index[get_identifier(recipe_data)] = recipe
return recipe_index
def get_override_list():
override_dirs = CFPreferencesCopyAppValue(
"RECIPE_OVERRIDE_DIRS", "com.github.autopkg"
)
if not override_dirs:
override_dirs = ["~/Library/AutoPkg/RecipeOverrides"]
elif isinstance(override_dirs, str):
override_dirs = [override_dirs]
overrides = []
for override_dir in override_dirs:
overrides.extend(
glob(os.path.join(override_dir, "*.recipe"))
+ glob(os.path.join(override_dir, "*/*.recipe"))
)
return overrides
def detect_differences(override, recipe_index):
# Skip excluded filenames.
filename = os.path.split(override)[-1].replace(".recipe", "")
if filename in EXCLUDED_OVERRIDES:
return
with open(override, "rb") as openfile:
override_data = plistlib.load(openfile)
parent_recipe = override_data.get("ParentRecipe")
if not recipe_index.get(parent_recipe) or not os.path.isfile(
recipe_index[parent_recipe]
):
print(
"%s: Could not find parent recipe with identifier %s. Skipping."
% (override_data["Identifier"], parent_recipe)
)
return
with open(recipe_index[parent_recipe], "rb") as openfile:
parent_data = plistlib.load(openfile)
announced = False
pkginfo = False
for key in override_data.get("Input"):
# Pkginfos must be processed in their own special way, below.
if key == "pkginfo":
# pkginfo = True
continue
# Skip any key we don't care about.
if key in EXCLUDED_KEYS:
continue
# Gather override and parent input key values.
o_value = override_data["Input"].get(key)
p_value = parent_data["Input"].get(key)
# If there's no parent value, check the parent's parent.
if not p_value:
gparent = parent_data.get("ParentRecipe")
if gparent and os.path.isfile(recipe_index[gparent]):
with open(recipe_index[gparent], "rb") as openfile:
gparent_data = plistlib.load(openfile)
p_value = gparent_data["Input"].get(key)
if not p_value:
ggparent = parent_data.get("ParentRecipe")
if ggparent and os.path.isfile(recipe_index[ggparent]):
with open(recipe_index[ggparent], "rb") as openfile:
ggparent_data = plistlib.load(openfile)
p_value = ggparent_data["Input"].get(key)
# TODO: Does this actually work?
# Skip any Adobe CC description placeholder.
if p_value == "Some description.":
continue
# Compare the values.
if o_value != p_value:
if not announced:
print("\n%s\nOverride file: %s" % ("-" * 80, override))
announced = True
print("\n %s differs:" % key)
print(" Override value: %s" % o_value)
print(" Parent value: %s" % p_value)
# Process pkginfo dicts similarly to the above.
if pkginfo:
for key in override_data["Input"]["pkginfo"]:
if key in EXCLUDED_KEYS:
continue
o_value = override_data["Input"]["pkginfo"].get(key)
p_value = parent_data["Input"]["pkginfo"].get(key)
if o_value != p_value:
if not announced:
print("\nOverride file: %s" % override)
announced = True
print(" pkginfo/%s differs:" % key)
print(" Override value: %s" % o_value)
print(" Parent value: %s" % p_value)
def main():
"""Main process."""
recipe_index = get_recipe_index()
overrides = sorted(get_override_list())
for override in overrides:
detect_differences(override, recipe_index)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment