3.0.0 (June 8, 2026)
Note
Release candidate lineage: AutoPkg 3.0.0 RC 4 is based on the 2.x improvements in prior versions while re-implementing some of the features previously included in 3.0.0 RCs 1-3. If a previous RC had a needed feature or fix that RC 4 lacks, please raise an issue to alert us.
Recipe map
AutoPkg now uses a "recipe map," an on-disk JSON cache (~/Library/AutoPkg/recipe_map.json by default) of every recipe and override on the local AutoPkg setup, indexed by identifier and shortname. This makes recipe resolution much faster on systems with many configured recipe repos. (This idea was introduced in 3.0.0 release candidates 1-3 but has been freshly reimplemented in 3.0.0 release candidate 4.)
You should notice a meaningful performance improvement during recipe run, info, search, and trust-info operations, especially if you have added a large number of recipe repos. The first use after install or repo-add/repo-update may be briefly slower while the map is built. The cache rebuilds automatically when missing or invalid, stays in sync through repo-add, repo-delete, repo-update, make-override, and new-recipe, and can be manually rebuilt with autopkg generate-recipe-map (useful in CI). When --search-dir or --override-dir differ from your preferences, resolution favors the values provided via CLI.
Troubleshooting recipe map issues:
- Run
autopkg generate-recipe-mapto force a clean rebuild. - Set
RECIPE_MAP_PATHpreference orAUTOPKG_RECIPE_MAP_PATHenv var to redirect the cache to a writable location (e.g. CI workspaces that don't use~/Library/AutoPkg). - Set
DISABLE_RECIPE_MAPpreference orAUTOPKG_DISABLE_RECIPE_MAP=1env var to bypass the cache entirely and fall back to the legacy on-disk scanners.
Note
When running AutoPkg with sudo (which is still not recommended outside of special use cases like CI), AUTOPKG_RECIPE_MAP_PATH is ignored and a warning is logged. Redirecting the map path via the RECIPE_MAP_PATH preference still works when running as root, but also emits a security warning. Consider stripping AUTOPKG_* from your sudoers env_keep to prevent unprivileged callers from influencing where AutoPkg writes.
Closes #869, #874, #884, #886, #893, #894, #898, #901, #903, #908, and #918 (#1027, thanks to @jgstew).
Python 3.11 (and plans for 3.12)
AutoPkg 3.0.0 includes Python 3.11.9, chosen because it introduces no breaking changes for AutoPkg or its bundled packages, and for its October 2027 security-support EOL.
The Python requirements are now split into a hand-maintained requirements.in (direct dependencies only) and a compiled requirements.txt lockfile. The bundled PyObjC 11.1 frameworks have been trimmed to the ones actually used, dropping CFNetwork, LaunchServices, OpenDirectory, and Quartz, which had no consumers in the AutoPkg core or in the autopkg org recipe repos. tomli has been dropped; Python 3.11 includes tomllib in the standard library. AutoPkg use of distutils has been removed. APLooseVersion now vendors the LooseVersion algorithm instead of subclassing it; version-comparison behavior is unchanged.
Warning
Custom processor authors: AutoPkg 3.1.0 plans to include Python 3.12, which removes distutils. If your processors still import from distutils, they will break on AutoPkg 3.1.0. AutoPkg's APLooseVersion is a drop-in replacement for LooseVersion; see the migration guide on the wiki. Pull requests have already been opened against affected processors in the autopkg org.
New clear-cache verb
AutoPkg 3.0.0 adds autopkg clear-cache for removing cached files when troubleshooting a recipe or reclaiming disk space. Pass a recipe name or identifier to clear that recipe's cache using the same recipe resolution behavior as run and info, or use autopkg clear-cache all to empty the configured cache directory. Pass --dry-run to preview what would be deleted without removing anything; use -v (with all) to list top-level items removed, or -vv to list individual files. (#1035)
URLDownloader metadata and hashing
URLDownloader now persists download metadata — including ETag, Last-Modified, and file size — to an .info.json sidecar file alongside each downloaded file. This improves reliability on filesystems and network volumes that don't support extended attributes (xattrs) (#978, thanks to @MScottBlake).
A new COMPUTE_HASHES input variable (default: False) enables on-demand computation of MD5, SHA1, and SHA256 hashes of the downloaded file. When enabled, hash values are available as output variables in subsequent processors.
Security improvements
Override trust information for scripts
Files in PkgCreator scripts directories are now included in recipe override trust information.
- Changes to preinstall/postinstall scripts or any other files bundled into packages will now trigger trust verification failures. Only git-tracked files are hashed when the scripts directory is inside a git repo, so untracked files like
.DS_Storewon't cause false trust failures. (#980)
Warning
Overrides of recipes that use PkgCreator scripts should be updated with autopkg update-trust-info to add script trust info. In AutoPkg 3.0.0, missing script trust info produces a warning; starting in AutoPkg 3.1.0, this will be a trust verification error.
CodeSignatureVerifier hardening
CodeSignatureVerifier now more effectively handles situations that previously allowed unintentionally skipping or weakening verification:
strict_verificationnow defaults toTrue(passes--strictto codesign).
Warning
This is a breaking change for recipes handling apps which fail strict verification (≈2.5% of total active recipes in the autopkg org). The suggested workaround is to set CodeSignatureVerifier's strict_verification argument to False specifically for those recipes. Pull requests have already been opened for affected recipe repositories.
- Verification failures now distinguish a wrong signing identity from an unsigned or invalid signature.
- For app bundles, a
requirementis now mandatory. For installer packages,expected_authority_namesis now mandatory. Without these, verification only confirmed that the item was signed by someone with a valid Developer ID (including a potential attacker), so recipes that omit these arguments now fail. - Typoed versions of the
requirementandexpected_authority_nameskeys now result in an error instead of a warning. - CodeSignatureVerifier and SignToolVerifier now always warn when verification is disabled via
DISABLE_CODE_SIGNATURE_VERIFICATION(previously silent at the default verbosity).autopkg runalso warns up front when it is set globally via environment variable, recipe list, or-k/--key. - CodeSignatureVerifier now fails clearly on non-macOS instead of silently skipping verification that requires macOS tools.
Improvements to audit
The audit verb has been strengthened to provide better signal for recipe practices that deserve close scrutiny:
auditnow recognizesURLDownloaderPythonas a downloader, so it flags a missingCodeSignatureVerifierfor recipes that download with it (previously onlyURLDownloaderandCURLDownloaderwere checked).auditnow recognizesAppPkgCreatorandChocolateyPackageras artifact creators, so modification processors preceding them are surfaced (previously onlyDmgCreator,FlatPkgPacker, andPkgCreatorwere recognized).auditnow reports path-sensitive recipe values that deserve closer inspection, including identifiers with path traversal markers, suspicious privileged install/copy paths,PkgRootCreatorparent-directory references, generic parent-directory traversal inCopier,FileMover,PathDeleter, andUnarchiverpath arguments, unsafe DMG pseudo-paths, and Chocolatey package identifiers or versions containing path separators.auditnow flags insecureftp:URLs alongsidehttp:URLs, and flagsChocolateyPackagerrecipes that explicitly use weak installer checksum algorithms (md5orsha1).
Path traversal protection
Multiple processors now confine paths to their intended directories, preventing a malicious or misconfigured recipe from reading or writing outside its expected territory:
RECIPE_CACHE_DIRis now confined toCACHE_DIR. A recipeIdentifiercontaining..or an absolute path could previously place the cache directory outsideCACHE_DIR, letting a recipe read or write another recipe's cache; such identifiers are now rejected.- AutoPkg now expands
CACHE_DIRto an absolute path, so~or relativeCACHE_DIRpreferences no longer create literal or working-directory-relative cache folders. - URLDownloader: a filename supplied via a server's
Content-Dispositionheader is now reduced to its base name, preventing a malicious server from using..or path separators to write the downloaded file outsidedownload_dir. - Paths that refer to files inside a DMG are now confined to the mounted image. DMG-relative paths containing
..or starting with/, and glob matches or symlinks that resolve outside the mount point, are now rejected. - PkgExtractor: the
IFPkgFlagDefaultLocationvalue from a package'sInfo.plistis now confined to the extraction root. A malicious package using..in this field could previously redirect extraction outside the intended directory; such paths are now rejected before any files are removed or extracted. - PkgRootCreator: the containment check for recipe-supplied
pkgdirsis now path-aware. The previous string-prefix check let a relative path such as../pkgroot-evilcreate directories outside the pkgroot (which would then be packaged); these are now correctly rejected. - Installer and InstallFromDMG now reject package/source paths outside the recipe cache or mounted disk image; InstallFromDMG also rejects setuid/setgid copy modes.
- The autopkginstalld privileged helper daemon now independently validates that package paths are confined to the recipe cache or disk images mounted under
/private/tmpbefore invoking the system installer. This check applies regardless of which processor initiates the install. - ChocolateyPackager now rejects package IDs and versions that could escape the build or output directories.
- SparkleUpdateInfoProvider now rejects non-HTTP(S) and loopback description links found in Sparkle feeds before copying their content into pkginfo descriptions.
- Recipe scanning, recipe-map building, and shared-processor loading now ignore symlinks that point outside configured search directories. Symlinked search directories themselves still work.
GitHub and search
- GitHub token handling is now more resilient: malformed or whitespace-only tokens are rejected before use and logged as a warning, and GET requests that receive a 401 automatically retry without authentication while alerting the user to regenerate their token (#1052)
GITHUB_TOKENis now available for recipe variable substitution when the token comes from~/.autopkg_gh_token, matching tokens stored in preferences (#923).- Improved search error in case of bad GitHub credentials (#1021, thanks to @MagerValp)
- Prevented KeyError during search when a GitHub error response omits the
statusfield (e.g. some credential errors return only amessagefield) - Fixed
autopkg searchcrash when a search cache entry is missing thesizefield (#1039)
Munki integration fixes
- MunkiOptionalReceiptEditor now routes pkginfo updates through the Munki repo plugin API, fixing silent data loss when using
GitFileRepoor other non-filesystem repo plugins (#1031). It now acceptsMUNKI_REPO,MUNKI_REPO_PLUGIN,MUNKILIB_DIR,force_munki_repo_lib, andrepo_subdirectoryinput variables (matching MunkiImporter's interface), and sets amunki_infooutput variable containing the updated pkginfo for use by downstream processors. - MunkiImporter now correctly handles multiple pkginfos for apps with the same installed path and app version.
- MunkiImporter now writes correct pkginfo paths when importing an uncataloged package already under the Munki repo's
pkgsdirectory. - MunkiImporter now avoids duplicate pkginfo filenames caused by leading or trailing whitespace in version strings.
Processor features and fixes
- DmgCreator: default
dmg_filesystemchanged fromHFS+toAPFSand defaultdmg_formatchanged fromUDZOtoULFO(lzfse compression).ULFOandULMOare now accepted as validdmg_formatvalues (#905, thanks to @erikng)
Note
APFS requires macOS 10.13 or later to mount. If you need to produce disk images compatible with older systems, set dmg_filesystem to HFS+ and dmg_format to UDZO explicitly.
- Processors that handle disk images now preserve original mount failures instead of masking them with subsequent "not mounted" cleanup errors.
- PkgCreator and AppPkgCreator: new
pkgbuild_argsinput variable allows forwarding additional flags (e.g.--filter,--large-payload) to thepkgbuildtool (#981) - macOS-only processors that rely on
hdiutil,pkgutil,xar, orpkgbuildnow fail with explicit platform errors on non-macOS instead of attempting to launch unavailable tools. - URLDownloaderPython now validates cached files against their actual size, exposes computed hashes, and preserves downloads when ETag or Last-Modified headers are missing.
- URLDownloader now applies
curl_common_opts, such as authorization headers, when prefetching filenames from authenticated URLs (#925, thanks to @n8felton) - PkgInfoCreator now finds relative Info.plist templates stored beside a recipe or parent recipe.
- PkgPayloadUnpacker: when both
dittoand theaafallback fail to extract a payload, the resultingProcessorErrornow reports the diagnostic output from both tools (previously only theaafailure was shown) and includesaa's stderr. Also fixed a latentUnboundLocalErrorthat occurred whendittocould not be executed (#1048, thanks to @n8felton). - InstallFromDMG now copies every requested item from disk images instead of stopping after the first.
- ChocolateyPackager now builds
installer_urlpackages with checksum fields and errors clearly wheninstaller_checksumis missing. - ChocolateyPackager now preserves Chocolatey command output and processor diagnostics when packaging fails.
- ChocolateyInstallGenerator now escapes single quotes in generated PowerShell string literals.
- URLGetter now reports curl diagnostics correctly when file downloads fail in binary mode.
- Removed some unused code from PkgRootCreator, URLDownloaderPython, and Versioner.
- CURLDownloader and CURLTextSearcher: these long-deprecated processors have been removed. They were no longer in use in any autopkg org recipes.
Other AutoPkg improvements and fixes
repo-updatenow migrates a recipe repo's local clone frommastertomainwhen the default branch was renamed upstream, so the repo keeps updating instead of silently stalling on a deleted remote branch.make-override --formatcan now be set globally via theRECIPE_OVERRIDE_FORMATpreference, so you don't need to pass--format yamlon every invocation (#1024, thanks to @grahampugh).- Processors invoked directly from the command line (a niche feature for debugging processors) now print their description and variable documentation when passed
-hor--help, or when run interactively with no arguments. Previously, an interactive no-argument invocation would wait for a plist on stdin. - YAML recipes are now parsed with a safer, more restrictive loader.
- Reduced the likelihood that float-looking version strings in YAML recipes (e.g.
VERSION: 1.0) will be silently coerced to a Python float instead of remaining a string, causing subtle inconsistencies compared to plist recipes (#1023). - Fixed a trust verification error message that printed the expected parent recipe list twice; it now shows both the expected and the actual parent recipe lists so it's clear what changed.
- Fixed
%key%variable substitution raising aTypeErrorwhen the recipe environment contains non-string values (#1038, thanks to @jgstew). - SparkleUpdateInfoProvider: fixed crash when handling
description_data, and corrected HTML closing tags fordescription