mirror of
https://github.com/grahampugh/macadmin-scripts.git
synced 2025-12-17 17:56:33 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6371c1852 | ||
|
|
4410b44f7b | ||
|
|
522fc7ff40 | ||
|
|
2cc8b89920 | ||
|
|
a66413ec7e | ||
|
|
85c643e684 | ||
|
|
b2570b4b0d | ||
|
|
eabf751816 | ||
|
|
73d7b90c7d | ||
|
|
77e507e435 | ||
|
|
ba1eeab9d0 | ||
|
|
1fc3224198 | ||
|
|
0ea9dc5a6a | ||
|
|
ca280304c0 | ||
|
|
944a01cb06 | ||
|
|
ad107a5593 | ||
|
|
074c1a5d7b | ||
|
|
4c4bdd8671 | ||
|
|
f2e9a63872 | ||
|
|
bd9205bde0 | ||
|
|
55e6345d5c | ||
|
|
a8a6f150c8 | ||
|
|
854798a010 | ||
|
|
ed6654e778 | ||
|
|
69ee37ae4e | ||
|
|
8b2f2c58b9 | ||
|
|
d77936d477 |
@ -3,9 +3,9 @@
|
||||
Some scripts that might be of use to macOS admins. Might be related to Munki;
|
||||
might not.
|
||||
|
||||
These are currently only supported using Apple's Python on macOS. There is no support for running these on Windows or Linux.
|
||||
These are only supported on macOS. There is no support for running these on Windows or Linux.
|
||||
|
||||
In macOS 12.3, Apple will be removing its Python 2.7 install. You'll need to provide your own Python to use these scripts. You may also need to install additional Python modules.
|
||||
In macOS 12.3, Apple stopped providung Python as part of macOS. You'll need to provide your own Python to use these scripts. You may also need to install additional Python modules.
|
||||
|
||||
#### getmacosipsws.py
|
||||
|
||||
@ -39,7 +39,7 @@ Product installation failed.
|
||||
Use a compatible Mac or select a different build compatible with your current hardware and try again. You may also have success running the script in a VM; the InstallationCheck script in versions of the macOS installer to date skips the checks (and returns success) when run on a VM.
|
||||
|
||||
##### Important note for Catalina+
|
||||
Catalina privacy protections might interfere with the operation of this tool if you run it from ~/Desktop, ~/Documents, ~/Downloads or other directories protected in Catalina. Consider using /Users/Shared (or subdirectory) as the "working space" for this tool.
|
||||
macOS privacy protections might interfere with the operation of this tool if you run it from ~/Desktop, ~/Documents, ~/Downloads or other directories protected in macOS Catalina or later. Consider using /Users/Shared (or subdirectory) as the "working space" for this tool.
|
||||
|
||||
|
||||
##### Alternate implementations
|
||||
|
||||
@ -42,7 +42,12 @@ except ImportError:
|
||||
from urlparse import urlsplit
|
||||
from xml.dom import minidom
|
||||
from xml.parsers.expat import ExpatError
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
try:
|
||||
from packaging.version import LegacyVersion
|
||||
except ImportError:
|
||||
# python <=3.9
|
||||
from distutils.version import LooseVersion as LegacyVersion
|
||||
|
||||
try:
|
||||
import xattr
|
||||
@ -80,6 +85,38 @@ DEFAULT_SUCATALOGS = {
|
||||
"index-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
|
||||
),
|
||||
"22": (
|
||||
"https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
|
||||
),
|
||||
"23": (
|
||||
"https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
|
||||
),
|
||||
"24": (
|
||||
"https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
|
||||
),
|
||||
"25": (
|
||||
"https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-16seed-16-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
|
||||
),
|
||||
"22": "https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
"23": "https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
"24": "https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
"25": "https://swscan.apple.com/content/catalogs/others/"
|
||||
"index-26-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9"
|
||||
"-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
}
|
||||
|
||||
SEED_CATALOGS_PLIST = (
|
||||
@ -260,11 +297,12 @@ def get_default_catalog(darwin_major=None):
|
||||
|
||||
def make_sparse_image(volume_name, output_path):
|
||||
"""Make a sparse disk image we can install a product to"""
|
||||
# note: for macOS 26 Tahoe we needed to increase the size
|
||||
cmd = [
|
||||
"/usr/bin/hdiutil",
|
||||
"create",
|
||||
"-size",
|
||||
"16g",
|
||||
"20g",
|
||||
"-fs",
|
||||
"HFS+",
|
||||
"-volname",
|
||||
@ -374,7 +412,12 @@ def install_product(dist_path, target_vol):
|
||||
# set CM_BUILD env var to make Installer bypass eligibilty checks
|
||||
# when installing packages (for machine-specific OS builds)
|
||||
os.environ["CM_BUILD"] = "CM_BUILD"
|
||||
cmd = ["/usr/sbin/installer", "-pkg", dist_path, "-target", target_vol]
|
||||
# cmd = ['/usr/sbin/installer', '-pkg', dist_path, '-target', target_vol]
|
||||
# a hack to work around a change in macOS 15.6+ since installing a .dist
|
||||
# file no longer works
|
||||
dist_dir = os.path.dirname(dist_path)
|
||||
install_asst_pkg = os.path.join(dist_dir, "InstallAssistant.pkg")
|
||||
cmd = ["/usr/sbin/installer", "-pkg", install_asst_pkg, "-target", target_vol]
|
||||
try:
|
||||
subprocess.check_call(cmd)
|
||||
except subprocess.CalledProcessError as err:
|
||||
@ -698,7 +741,7 @@ def os_installer_product_info(
|
||||
def get_latest_version(current_item, latest_item):
|
||||
"""Compares versions between two values and returns the latest (highest) value"""
|
||||
try:
|
||||
if LooseVersion(current_item) > LooseVersion(latest_item):
|
||||
if LegacyVersion(current_item) > LegacyVersion(latest_item):
|
||||
return current_item
|
||||
else:
|
||||
return latest_item
|
||||
@ -1062,7 +1105,7 @@ def main():
|
||||
|
||||
# determine the latest valid build ID and select this
|
||||
# when using auto, os and version options
|
||||
if args.auto or args.version or args.os:
|
||||
if args.auto or args.version or args.os or args.list:
|
||||
if args.beta or "Beta" not in product_info[product_id]["title"]:
|
||||
try:
|
||||
latest_valid_build
|
||||
@ -1132,10 +1175,10 @@ def main():
|
||||
valid_build_found is False
|
||||
and not args.build
|
||||
and not args.current
|
||||
and not args.validate
|
||||
and args.validate
|
||||
and not args.list
|
||||
):
|
||||
print("No valid build found for this hardware")
|
||||
print("No valid build found for this computer")
|
||||
exit(0)
|
||||
|
||||
# clear content directory in workdir if requested
|
||||
@ -1147,6 +1190,18 @@ def main():
|
||||
|
||||
# Output a plist of available updates and quit if list option chosen
|
||||
if args.list:
|
||||
if args.os:
|
||||
print("\nBuild checked against OS filter: %s" % (args.os))
|
||||
pl["validity_filter"] = "OS: %s" % (args.os)
|
||||
elif args.version:
|
||||
print("\nBuild checked against version filter: %s" % (args.version))
|
||||
pl["validity_filter"] = "Version: %s" % (args.version)
|
||||
try:
|
||||
print("\nLatest valid build: %s (# %s)" % (latest_valid_build, answer))
|
||||
pl["latest_valid_build"] = latest_valid_build
|
||||
except NameError:
|
||||
pl["latest_valid_build"] = ""
|
||||
print("\nNo valid build found")
|
||||
write_plist(pl, output_plist)
|
||||
print("\nValid seeding programs are: %s\n" % ", ".join(get_seeding_programs()))
|
||||
exit(0)
|
||||
@ -1158,23 +1213,21 @@ def main():
|
||||
except NameError:
|
||||
print(
|
||||
"\n"
|
||||
"Build %s is not available. "
|
||||
"A valid installer for build %s is not available for this computer. "
|
||||
"Run again without --build argument "
|
||||
"to select a valid build to download "
|
||||
"or run without --validate option to download anyway.\n" % args.build
|
||||
)
|
||||
exit(0)
|
||||
else:
|
||||
print(
|
||||
"\n" "Build %s available. Downloading #%s...\n" % (args.build, answer)
|
||||
)
|
||||
print("\n" "Build %s valid. Downloading #%s...\n" % (args.build, answer))
|
||||
elif args.current:
|
||||
try:
|
||||
answer
|
||||
except NameError:
|
||||
print(
|
||||
"\n"
|
||||
"Build %s is not available. "
|
||||
"A valid installer for build %s is not available for this computer. "
|
||||
"Run again without --current argument "
|
||||
"to select a valid build to download.\n" % build_info[0]
|
||||
)
|
||||
@ -1222,7 +1275,7 @@ def main():
|
||||
except NameError:
|
||||
print(
|
||||
"\n"
|
||||
"Version %s is not available. "
|
||||
"A valid installer for version %s is not available for this computer. "
|
||||
"Run again without --version argument "
|
||||
"to select a valid build to download.\n" % args.version
|
||||
)
|
||||
@ -1238,7 +1291,7 @@ def main():
|
||||
except NameError:
|
||||
print(
|
||||
"\n"
|
||||
"OS %s is not available. "
|
||||
"A valid installer for OS %s is not available for this computer. "
|
||||
"Run again without --os argument "
|
||||
"to select a valid build to download.\n" % args.os
|
||||
)
|
||||
@ -1254,7 +1307,7 @@ def main():
|
||||
except NameError:
|
||||
print(
|
||||
"\n"
|
||||
"No valid version available. "
|
||||
"No valid version available for this computer. "
|
||||
"Run again without --auto argument "
|
||||
"to select a valid build to download.\n"
|
||||
)
|
||||
@ -1344,7 +1397,9 @@ def main():
|
||||
% seeding_program
|
||||
)
|
||||
xattr.setxattr(
|
||||
installer_app, "SeedProgram", seeding_program.encode()
|
||||
installer_app,
|
||||
"SeedProgram",
|
||||
seeding_program.encode("UTF-8").encode(),
|
||||
)
|
||||
print("Product downloaded and installed to %s" % sparse_diskimage_path)
|
||||
if args.raw:
|
||||
|
||||
62
munki_bundle_pkg_finder.py
Executable file
62
munki_bundle_pkg_finder.py
Executable file
@ -0,0 +1,62 @@
|
||||
#!/usr/local/munki/munki-python
|
||||
|
||||
import os
|
||||
import plistlib
|
||||
import sys
|
||||
|
||||
sys.path.append("/usr/local/munki")
|
||||
|
||||
from munkilib import dmgutils
|
||||
from munkilib import pkgutils
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print('Need exactly one parameter: path to a munki repo!', file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
repo_path = sys.argv[1]
|
||||
|
||||
all_catalog = os.path.join(repo_path, "catalogs/all")
|
||||
|
||||
with open(all_catalog, mode="rb") as FILE:
|
||||
all_items = plistlib.load(FILE)
|
||||
|
||||
dmg_items = [{"name": item["name"],
|
||||
"version": item["version"],
|
||||
"location": item["installer_item_location"],
|
||||
"package_path": item.get("package_path", "")}
|
||||
for item in all_items
|
||||
if item.get("installer_item_location", "").endswith(".dmg") and
|
||||
item.get("installer_type") is None]
|
||||
|
||||
items_with_bundle_style_pkgs = []
|
||||
for item in dmg_items:
|
||||
full_path = os.path.join(repo_path, "pkgs", item["location"])
|
||||
print("Checking %s..." % full_path)
|
||||
mountpoints = dmgutils.mountdmg(full_path)
|
||||
if mountpoints:
|
||||
pkg_path = item["package_path"]
|
||||
if pkg_path:
|
||||
itempath = os.path.join(mountpoints[0], pkg_path)
|
||||
if os.path.isdir(itempath):
|
||||
print("***** %s--%s has a bundle-style pkg"
|
||||
% (item["name"], item["version"]))
|
||||
items_with_bundle_style_pkgs.append(item)
|
||||
else:
|
||||
for file_item in os.listdir(mountpoints[0]):
|
||||
if pkgutils.hasValidInstallerItemExt(file_item):
|
||||
itempath = os.path.join(mountpoints[0], file_item)
|
||||
if os.path.isdir(itempath):
|
||||
print("***** %s--%s has a bundle-style pkg"
|
||||
% (item["name"], item["version"]))
|
||||
items_with_bundle_style_pkgs.append(item)
|
||||
break
|
||||
dmgutils.unmountdmg(mountpoints[0])
|
||||
else:
|
||||
print("No filesystems mounted from %s" % full_path)
|
||||
continue
|
||||
|
||||
print("Found %s items with bundle-style pkgs."
|
||||
% len(items_with_bundle_style_pkgs))
|
||||
for item in sorted(items_with_bundle_style_pkgs, key=lambda d: d["name"]):
|
||||
print("%s--%s"% (item["name"], item["version"]))
|
||||
print(" %s" % item["location"])
|
||||
Loading…
x
Reference in New Issue
Block a user