Munki: How to remove cruft
An issue with Munki, especially if using AutoPkg, is that you can quickly amass many versions of software packages, which can fill up your repository volume. There are no automated ways of clearing out old versions of software, but in many cases there is no reason to keep them.
Removing a package involves the following process:
- Locate the imported package and delete it
- Locate the associated pkginfo file and delete it
makecatalogs
If you’re organised, you’ll know exactly where every package is in the subdirectory structure of your Munki repository. But the repository can get hard to navigate. In which folder does Autopkg put Java 8? To where did your colleague import SPSS?
Use a GUI!
One way of removing packages is using the MunkiAdmin application. Simply right- or ctrl-click on an item in the Packages list, and select Delete Package. MunkiAdmin offers to delete the package and associated pkginfo
file, and potentially the icon.
Command-line alternative?
If you don’t wish to use MunkiAdmin, for instance if you are working remotely on a server via ssh
, you may wish for an easy way to find and remove old packages using the command line. The munkitools don’t include a tool for this task. So I wrote a script to find items in the Munki repository that match an input, list the items in order of filename (so that associated .pkg and .plist files are adjacent in the list), and offer to delete each one in term.
Pressing y
deletes the item and moves on to the next item in the list. Pressing n
or any other character (except q
) skips the item and moves on to the next. Pressing q
skips to the end of the list. After the end of the list has been reached, if anything has been deleted, makecatalogs
is run (for this to work, this script needs to be run on a Mac with munkitools installed and configured to point to your munki repository).
#!/bin/bash
### A script to search your Munki repo and offer to delete items. Use with care!
### It will search all directories including pkgs, pkgsinfo and icons.
### Syntax: /path/to/munkirm -d <search-term>
### For example, /path/to/munkirm -d xcode
### Search is case insensitive.
### Options are y or Y to delete, n, N or anything else to skip, and q or Q to quit
### If any changes are made, `makecatalogs` is run
### Put munkirm in /usr/local/munki/ if you wish to run from all directories
# Munki repo - change to match your path
MUNKI_REPO=`defaults read ~/Library/Preferences/com.googlecode.munki.munkiimport.plist repo_path`
# Introductions
echo
echo "----------------------------"
echo " MUNKI FILE REMOVAL TOOL"
echo "----------------------------"
echo "Usage: munkirm -d <string>"
echo "<string> is case-insensitive"
echo "part strings OK, e.g. goog"
echo
# First level
while getopts ":d:" opt; do # Check to see if munkiimport is configured
if [ -z ${MUNKI_REPO} ]; then
echo "### munkiimport not configured. Run `munkiimport --configure`"
echo
exit 1
fi
# Check to see if the repository is mounted
if [ ! -d ${MUNKI_REPO} ]; then
echo "### Munki repository not mounted! Cannot continue"
echo
exit 1
fi
case $opt in
d)
# echo "-d was triggered, Parameter: $OPTARG" >&2
PKG="$OPTARG"
# write find results to temporary file
#find $MUNKI_REPO -type f -iname "*$PKG*" > /tmp/list.txt
find $MUNKI_REPO -type f -iname "*$PKG*" | awk -v FS=/ -v OFS=/ '{ print $NF,$0 }' | sort -n -t / | cut -f2- -d/ > /tmp/list.txt
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
if [ -s /tmp/list.txt ]; then # Print the results first
echo
echo "Found these files:"
echo
cat /tmp/list.txt
echo # Now offer up each file for deletion
for file in `cat /tmp/list.txt`; do
read -p "Delete $file (y/n/q)? " -n 1 input
case $input in
y|Y ) echo
rm -r $file
echo "$file Deleted!"
echo
REMAKE=1
;;
q|Q ) echo
echo "skipped to end"
echo
break
;; \* ) echo
echo "skipped"
echo
;;
esac
done
else # No match, therefore no file
echo "Files not found!"
echo
fi
rm /tmp/list.txt
# Update the repo if required
if [[$REMAKE == 1]]; then
if hash makecatalogs 2>/dev/null; then
echo "### Catalogs have changed - running makecatalogs"
echo
/usr/local/munki/makecatalogs $MUNKI_REPO
else
echo "### Catalogs have changed but you don't have makecatalogs installed!"
echo "### Now run makecatalogs on a Mac with munkitools installed to effect the changes"
echo
fi
else
echo "### Catalogs have not changed. Exiting..."
echo
fi
exit 0
Make the script executable:
$ chmod +x /path/to/munkirm
To run the script (in this example, we search for ‘java’):
$ /path/to/munkirm -d java
Example output:
$ /path/to/munkirm -d chrome
---
## MUNKI FILE REMOVAL TOOL
Usage: munkirm -d
is case-insensitive
part strings OK, e.g. goog
Found these files:
/Volumes/munki_repo/pkgs/apps/GoogleChrome-40.0.2214.115.dmg
/Volumes/munki_repo/pkgsinfo/apps/GoogleChrome-40.0.2214.115.plist
/Volumes/munki_repo/pkgs/apps/GoogleChrome-41.0.2272.118.dmg
/Volumes/munki_repo/pkgsinfo/apps/GoogleChrome-41.0.2272.118.plist
/Volumes/munki_repo/pkgs/apps/GoogleChrome-42.0.2311.90.dmg
/Volumes/munki_repo/pkgsinfo/apps/GoogleChrome-42.0.2311.90.plist
/Volumes/munki_repo/icons/GoogleChrome.png
Delete /Volumes/munki_repo/pkgs/apps/GoogleChrome-40.0.2214.115.dmg (y/n/q)? y
/Volumes/munki_repo/pkgs/apps/GoogleChrome-40.0.2214.115.dmg Deleted!
Delete /Volumes/munki_repo/pkgsinfo/apps/GoogleChrome-40.0.2214.115.plist (y/n/q)? y
/Volumes/munki_repo/pkgsinfo/apps/GoogleChrome-40.0.2214.115.plist Deleted!
Delete /Volumes/munki_repo/pkgs/apps/GoogleChrome-41.0.2272.118.dmg (y/n/q)? n
skipped
Delete /Volumes/munki_repo/pkgsinfo/apps/GoogleChrome-41.0.2272.118.plist (y/n/q)? q
skipped to end
### Catalogs have changed - running makecatalogs
Hashing Adobe Acrobat XI Pro_3.png...
Hashing Adobe-Premiere.png...
... [makecatalogs output] ...
Created catalog /Volumes/munki_repo/catalogs/standard...
If you want to run this tool from any directory without requiring a path, copy it into /usr/local/munki/
:
$ sudo cp /path/to/munkirm /usr/local/munki/