Last active
August 29, 2015 14:26
-
-
Save homebysix/58a0eea77e882ec54d00 to your computer and use it in GitHub Desktop.
show_unused_munki_packages.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Name: show_unused_munki_packages.py | |
Description: Given a path to a Munki repo, this script will tell you | |
which package names are unused in any manifests. Useful for | |
detecting things that were imported into Munki but never | |
added to a manifest. | |
Author: Elliot Jordan <[email protected]> | |
Created: 2015-07-30 | |
Last Modified: 2015-08-03 | |
Version: 1.0.7 | |
""" | |
from xml.parsers.expat import ExpatError | |
import os | |
import plistlib | |
from pprint import pprint | |
import sys | |
def process_manifest(manifest, packages_in_manifests): | |
for item in ("managed_installs", "managed_uninstalls", "managed_updates", "optional_installs"): | |
try: | |
for package in manifest[item]: | |
if package not in packages_in_manifests: | |
packages_in_manifests.append(package) | |
except Exception: | |
continue | |
try: | |
if manifest["conditional_items"]: | |
for conditional_item in manifest["conditional_items"]: | |
process_manifest(conditional_item, packages_in_manifests) | |
except Exception: | |
pass | |
def main(): | |
munki_repo = raw_input( | |
"Please enter the path to your Munki repo [defaults to /Users/Shared/munki_repo]: ") | |
if munki_repo == "": | |
munki_repo = "/Users/Shared/munki_repo" | |
packages_in_repo = [] | |
requirements_in_repo = [] | |
for dirpath, dirnames, filenames in os.walk(os.path.join(munki_repo, "pkgsinfo")): | |
for dirname in dirnames: | |
# Skip directories that start with a period (e.g. ".git") | |
if dirname.startswith("."): | |
dirnames.remove(dirname) | |
for filename in filenames: | |
# Skip files that start with a period (e.g. ".DS_Store") | |
if filename.startswith("."): | |
continue | |
filepath = os.path.join(dirpath, filename) | |
try: | |
pkginfo = plistlib.readPlist(filepath) | |
try: | |
if pkginfo["name"] not in packages_in_repo: | |
if "update_for" not in pkginfo: | |
if "installer_type" not in pkginfo: | |
packages_in_repo.append(pkginfo["name"]) | |
elif pkginfo["installer_type"] != "apple_update_metadata": | |
packages_in_repo.append(pkginfo["name"]) | |
if "requires" in pkginfo: | |
for requirement in pkginfo["requires"]: | |
if requirement not in requirements_in_repo: | |
requirements_in_repo.append(requirement) | |
except KeyError: | |
continue | |
except ExpatError: | |
print >> sys.stderr, "Could not parse %s" % os.path.join( | |
dirpath, filename) | |
packages_in_manifests = [] | |
for dirpath, dirnames, filenames in os.walk(os.path.join(munki_repo, "manifests")): | |
for dirname in dirnames: | |
# Skip directories that start with a period (e.g. ".git") | |
if dirname.startswith("."): | |
dirnames.remove(dirname) | |
for filename in filenames: | |
# Skip files that start with a period (e.g. ".DS_Store") | |
if filename.startswith("."): | |
continue | |
filepath = os.path.join(dirpath, filename) | |
try: | |
manifest = plistlib.readPlist(filepath) | |
process_manifest(manifest, packages_in_manifests) | |
except ExpatError: | |
print >> sys.stderr, "Could not parse %s" % os.path.join( | |
dirpath, filename) | |
unused_packages = list(set(packages_in_repo) - set(requirements_in_repo) - set(packages_in_manifests)) | |
print "\n UNUSED PACKAGES:\n" | |
pprint(unused_packages) | |
if __name__ == '__main__': | |
main() |
After making that change, I get the following errors:
Could not parse /Users/Shared/munki_repo/manifests/.DS_Store
Could not parse /Users/Shared/munki_repo/manifests/.git/....
and lots more lines for things in .git.
So you might want to do some filtering on filenames before trying to feed them to plistlib.readPlist.
It identified all my apple_update_metadata items as unused packages -- these should never be added to a manifest, so they should probably be filtered out of the list.
@gregneagle - Give it another try. I updated the script above, and tested it with my own git-tracked manifests.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I get an exception: xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 0 at
File "show_unused_munki_packages.py", line 61, in main
manifest = plistlib.readPlist(os.path.join(directory, file))
There's probably a bad manifest in my repo.
I suggest wrapping that bit in a try/except: