Lab Software Update
Software reaches the lab Macs in three hops. An installer is imported into a
Munki repository on the admin Mac; publish.sh copies that repository to the
lab server; each lab Mac notices the change on its next hourly check and
installs it. The whole point is that updating Fusion for the entire lab is a
download, one munkiimport, and one ./publish.sh, instead of a trip to every
desk.
This page covers two things: rebuilding the admin Mac if it dies, and the routine for adding or updating a title. If you're here for the routine, skip to Add a New Title.
How the Repository Is Organized
Everything is a folder of plain files. Munki has no database.
| Folder | Contents | Who writes it |
|---|---|---|
pkgs/ |
The installers themselves, named <name>-<version>.pkg |
munkiimport |
pkgsinfo/ |
One plist per installer describing it: name, version, how to tell if it's installed | munkiimport, then you touch it up |
catalogs/ |
Generated index of everything in pkgsinfo/; production is the one clients read |
makecatalogs (automatic in publish.sh) |
manifests/ |
Which titles go on which Macs; site_default is the one every lab Mac uses |
manifestutil |
icons/ |
<name>.png, shown in Managed Software Center |
munkiimport, optional |
A title is one piece of software across all its versions, identified by its
name (for example fusion). A new version of a title is a new pkg and a new
pkginfo with the same name and a higher version; Munki works out the
upgrade from there.
Set Up the Admin Mac
Steps are numbered continuously through this page.
-
Install the Munki tools from the current release. The one
.pkginstalls the admin tools and the client together. Restart when asked. -
Put the tools on your path.
-
Recover the repository. If the lab server is alive, it has a complete copy of everything except
publish.sh:If the server is also gone, start empty and rebuild it from the installers:
-
Tell
munkiimportwhere the repository is.Prompt Response Repo URL file:///Users/<your username>/munki_repo(type the real path;~is not expanded here)pkginfo extension leave blank pkginfo editor nano(or your editor's command-line name)Default catalog productionRepo access plugin FileRepo -
Give this Mac passwordless SSH to the server.
-
Create the publish script. It rebuilds the catalogs, mirrors the repository to the server, and sets permissions nginx can read.
cat > ~/munki_repo/publish.sh <<'SCRIPT' #!/bin/zsh set -e /usr/local/munki/makecatalogs ~/munki_repo rsync -rltv --delete \ --exclude '.DS_Store' --exclude 'publish.sh' \ ~/munki_repo/ pi@engineering.local:/srv/munki_repo/ ssh pi@engineering.local 'chmod -R u=rwX,go=rX /srv/munki_repo' SCRIPT chmod +x ~/munki_repo/publish.shThis Is Not GNU rsync
macOS ships
openrsyncunder the namersync. It does not understand--chmod, and-awould copy each file's mode from the Mac, where a downloaded installer is sometimes600. The-rltvflags plus the trailingchmodover SSH are what make the files readable by nginx.--deleteis intentional: this Mac is the single source of truth. -
If you started empty in step 3, create the lab manifest; otherwise skip.
-
Publish once to prove the pipeline.
Your terminal should look like this: the
rsyncfile list, thenHTTP/1.1 200 OK.
Add a New Title
Every title goes through the same five moves: import, touch up the pkginfo, add it to the manifest, publish, confirm on one lab Mac. Updating an existing title is the same minus the manifest step; see Update an Existing Title.
-
Download the installer. Munki wants a
.pkg. Most vendors offer one, and Autodesk's lab install for Fusion is one. A.dmgthat contains a.pkgalso imports directly. -
Import it. The prompts below are in the order
munkiimportasks them. Fields marked optional can be left blank by pressing Enter.Prompt Response Notes Item name The bare product name, lowercase, no spaces or version: thonny,fusion,prusaslicerOverwrite the suggestion. munkiimportguesses from the filename and usually includes a version or architecture. This value must stay identical across versions; it is how manifests and icons find the titleDisplay name The product's proper name: Thonny,Autodesk FusionWhat students see in Managed Software Center Description One plain sentence: 3D CAD used in the engineering labsOptional. Shown under the display name Version Accept the detected value Only correct it if you know the pkg's version metadata is wrong Category CAD,Programming, or blankOptional. Only groups items in Managed Software Center; has no effect on what installs where. Avoid class or teacher names here; that's the manifest's job Developer Autodesk, or blankOptional. Cosmetic Unattended install TrueInstalls without a student clicking anything. Munki still waits if the app is running or a restart is needed Unattended uninstall Trueif the pkg has a clean uninstaller,Falsefor large suites like FusionMatters only if you later remove the title Catalogs productionThe only catalog clients read Import this item? yUpload item to subdirectory blank Keeps pkgs/flatUse this directory? (first import only) yAppears once, on an empty repo Attempt to create a product icon? yOptional. Harmless if it finds nothing Edit pkginfo before upload? NThe touch-ups in step 11 are easier afterward Rebuild catalogs? yExpected Noise on an Empty Repo
The very first import prints
Could not read existing catalogs: Could not read 'all' catalog. That catalog doesn't exist until the first rebuild. Carry on.Worked Example: Autodesk Fusion
Prompt Response Item name fusionDisplay name Autodesk FusionDescription 3D CAD used in the engineering labsVersion 2705.1.15(accepted)Category CADDeveloper AutodeskUnattended install TrueUnattended uninstall FalseCatalogs productionUpload item to subdirectory blank Attempt to create a product icon? yEdit pkginfo before upload? NRebuild catalogs? y -
Touch up the pkginfo. Three things, every time. Set the two variables first; they're used by all three.
Rename the pkg to
<name>-<version>.pkgand record the new location.munkiimportkeeps the vendor's filename, which for Autodesk contains spaces that break plain URLs.OLD=$(ls pkgs | grep -i "$NAME" | grep -v "^$NAME-$VER.pkg$") [ -n "$OLD" ] && mv "pkgs/$OLD" "pkgs/$NAME-$VER.pkg" /usr/libexec/PlistBuddy -c "Set :installer_item_location $NAME-$VER.pkg" "pkgsinfo/$NAME-$VER"Add
installsifmunkiimportdidn't. This tells Munki to decide "installed or not" by looking for the application itself, rather than by the package receipt. Students drag apps to the Trash; receipts survive that, and a receipts-only item would never be reinstalled.grep -c '<key>installs</key>' "pkgsinfo/$NAME-$VER" # 0 means add it APP="Autodesk Fusion" # app name without .app /usr/libexec/PlistBuddy \ -c 'Add :installs array' \ -c 'Add :installs:0 dict' \ -c 'Add :installs:0:type string application' \ -c "Add :installs:0:path string /Applications/$APP.app" \ -c "Add :installs:0:CFBundleShortVersionString string $VER" \ -c 'Add :installs:0:version_comparison_key string CFBundleShortVersionString' \ "pkgsinfo/$NAME-$VER"Check the Version Key First
CFBundleShortVersionStringis the usual key, but confirm on a Mac that already has the app:defaults read "/Applications/$APP.app/Contents/Info.plist" CFBundleShortVersionStringshould print a version in the same style as$VER. If onlyCFBundleVersiondoes, use that key in both places above. Comparing the wrong key gives either a perpetual reinstall or a perpetual "already installed."Add
blocking_applicationsso Munki won't replace the app while a student has it open; it waits and retries next cycle. -
Add the title to the lab manifest. New titles only; an updated version of an existing title is already listed.
-
Publish, and confirm the server has the whole file.
./publish.sh curl -sI "http://engineering.local/pkgs/$NAME-$VER.pkg" | grep -i -E 'HTTP|content-length' ls -l "pkgs/$NAME-$VER.pkg"Your terminal should look like this:
200 OK, and the two byte counts match. A 3.8 GB publish over Wi-Fi takes a while; that's the one slow leg. -
Confirm on one lab Mac before trusting the hourly cycle. Quit the app first if it's open.
sudo managedsoftwareupdate -vv --checkonly 2>&1 | grep -i -E "$NAME|install" sudo managedsoftwareupdate --installonlyThe check should list
+ <name>-<version>. The install runs quietly; Fusion takes several minutes. Afterward the app's About box, ordefaults read "/Applications/$APP.app/Contents/Info.plist" CFBundleShortVersionString, shows the new version.
Done
Every other lab Mac picks it up within the hour, provided the app isn't running there at that moment.
Update an Existing Title
Same as adding, with two differences.
-
Run steps 9 through 11 with the same Item name as before (
fusionstaysfusion). The higher version is what makes it an upgrade. -
Skip step 12; the manifest already lists the title. Do steps 13 and 14.
-
Every few updates, prune superseded versions so the server doesn't fill up.
repocleankeeps the two newest versions of each title and shows you what it intends to delete before doing it.
Remove a Title
-
Take it out of the manifest and publish. Existing installs stay on the Macs; Munki simply stops managing them.
To actively uninstall it from the lab as well, use
manifestutil add-pkg $NAME --section managed_uninstalls --manifest site_defaultinstead of removing it. That only works for titles imported with Unattended uninstallTrueand a working uninstall method.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
curl -I on a pkg returns a body of about 146 bytes |
nginx 404: the filename doesn't match installer_item_location (spaces, vendor name) |
Redo the rename in step 11, publish |
curl -I returns 403 Forbidden |
File mode 600 reached the server |
./publish.sh again; its chmod line fixes it |
| Client says No changes to managed software but the app is missing | Receipts-only pkginfo; the receipt survived a drag to the Trash | Add installs (step 11), publish |
| Client never upgrades, or reinstalls every hour | Wrong version_comparison_key |
See the warning in step 11 |
-vv --checkonly shows 404s for manifests/<serial> and manifests/<hostname> |
Normal: Munki tries those before site_default |
Nothing. A 404 on site_default or catalogs/production is the one that matters |
| Install skipped with blocking application running | The app is open on that Mac | Nothing; Munki retries next cycle |
rsync: --chmod=...: invalid argument |
Someone restored a GNU-style publish.sh |
Use the script in step 6 |