mirror of
https://github.com/grahampugh/macadmin-scripts.git
synced 2025-12-18 02:06:26 +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;
|
Some scripts that might be of use to macOS admins. Might be related to Munki;
|
||||||
might not.
|
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
|
#### 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.
|
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+
|
##### 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
|
##### Alternate implementations
|
||||||
|
|||||||
@ -42,7 +42,12 @@ except ImportError:
|
|||||||
from urlparse import urlsplit
|
from urlparse import urlsplit
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
from xml.parsers.expat import ExpatError
|
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:
|
try:
|
||||||
import xattr
|
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"
|
"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"
|
"-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 = (
|
SEED_CATALOGS_PLIST = (
|
||||||
@ -260,11 +297,12 @@ def get_default_catalog(darwin_major=None):
|
|||||||
|
|
||||||
def make_sparse_image(volume_name, output_path):
|
def make_sparse_image(volume_name, output_path):
|
||||||
"""Make a sparse disk image we can install a product to"""
|
"""Make a sparse disk image we can install a product to"""
|
||||||
|
# note: for macOS 26 Tahoe we needed to increase the size
|
||||||
cmd = [
|
cmd = [
|
||||||
"/usr/bin/hdiutil",
|
"/usr/bin/hdiutil",
|
||||||
"create",
|
"create",
|
||||||
"-size",
|
"-size",
|
||||||
"16g",
|
"20g",
|
||||||
"-fs",
|
"-fs",
|
||||||
"HFS+",
|
"HFS+",
|
||||||
"-volname",
|
"-volname",
|
||||||
@ -374,7 +412,12 @@ def install_product(dist_path, target_vol):
|
|||||||
# set CM_BUILD env var to make Installer bypass eligibilty checks
|
# set CM_BUILD env var to make Installer bypass eligibilty checks
|
||||||
# when installing packages (for machine-specific OS builds)
|
# when installing packages (for machine-specific OS builds)
|
||||||
os.environ["CM_BUILD"] = "CM_BUILD"
|
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:
|
try:
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
except subprocess.CalledProcessError as err:
|
except subprocess.CalledProcessError as err:
|
||||||
@ -698,7 +741,7 @@ def os_installer_product_info(
|
|||||||
def get_latest_version(current_item, latest_item):
|
def get_latest_version(current_item, latest_item):
|
||||||
"""Compares versions between two values and returns the latest (highest) value"""
|
"""Compares versions between two values and returns the latest (highest) value"""
|
||||||
try:
|
try:
|
||||||
if LooseVersion(current_item) > LooseVersion(latest_item):
|
if LegacyVersion(current_item) > LegacyVersion(latest_item):
|
||||||
return current_item
|
return current_item
|
||||||
else:
|
else:
|
||||||
return latest_item
|
return latest_item
|
||||||
@ -1062,7 +1105,7 @@ def main():
|
|||||||
|
|
||||||
# determine the latest valid build ID and select this
|
# determine the latest valid build ID and select this
|
||||||
# when using auto, os and version options
|
# 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"]:
|
if args.beta or "Beta" not in product_info[product_id]["title"]:
|
||||||
try:
|
try:
|
||||||
latest_valid_build
|
latest_valid_build
|
||||||
@ -1132,10 +1175,10 @@ def main():
|
|||||||
valid_build_found is False
|
valid_build_found is False
|
||||||
and not args.build
|
and not args.build
|
||||||
and not args.current
|
and not args.current
|
||||||
and not args.validate
|
and args.validate
|
||||||
and not args.list
|
and not args.list
|
||||||
):
|
):
|
||||||
print("No valid build found for this hardware")
|
print("No valid build found for this computer")
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
# clear content directory in workdir if requested
|
# 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
|
# Output a plist of available updates and quit if list option chosen
|
||||||
if args.list:
|
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)
|
write_plist(pl, output_plist)
|
||||||
print("\nValid seeding programs are: %s\n" % ", ".join(get_seeding_programs()))
|
print("\nValid seeding programs are: %s\n" % ", ".join(get_seeding_programs()))
|
||||||
exit(0)
|
exit(0)
|
||||||
@ -1158,23 +1213,21 @@ def main():
|
|||||||
except NameError:
|
except NameError:
|
||||||
print(
|
print(
|
||||||
"\n"
|
"\n"
|
||||||
"Build %s is not available. "
|
"A valid installer for build %s is not available for this computer. "
|
||||||
"Run again without --build argument "
|
"Run again without --build argument "
|
||||||
"to select a valid build to download "
|
"to select a valid build to download "
|
||||||
"or run without --validate option to download anyway.\n" % args.build
|
"or run without --validate option to download anyway.\n" % args.build
|
||||||
)
|
)
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
print(
|
print("\n" "Build %s valid. Downloading #%s...\n" % (args.build, answer))
|
||||||
"\n" "Build %s available. Downloading #%s...\n" % (args.build, answer)
|
|
||||||
)
|
|
||||||
elif args.current:
|
elif args.current:
|
||||||
try:
|
try:
|
||||||
answer
|
answer
|
||||||
except NameError:
|
except NameError:
|
||||||
print(
|
print(
|
||||||
"\n"
|
"\n"
|
||||||
"Build %s is not available. "
|
"A valid installer for build %s is not available for this computer. "
|
||||||
"Run again without --current argument "
|
"Run again without --current argument "
|
||||||
"to select a valid build to download.\n" % build_info[0]
|
"to select a valid build to download.\n" % build_info[0]
|
||||||
)
|
)
|
||||||
@ -1222,7 +1275,7 @@ def main():
|
|||||||
except NameError:
|
except NameError:
|
||||||
print(
|
print(
|
||||||
"\n"
|
"\n"
|
||||||
"Version %s is not available. "
|
"A valid installer for version %s is not available for this computer. "
|
||||||
"Run again without --version argument "
|
"Run again without --version argument "
|
||||||
"to select a valid build to download.\n" % args.version
|
"to select a valid build to download.\n" % args.version
|
||||||
)
|
)
|
||||||
@ -1238,7 +1291,7 @@ def main():
|
|||||||
except NameError:
|
except NameError:
|
||||||
print(
|
print(
|
||||||
"\n"
|
"\n"
|
||||||
"OS %s is not available. "
|
"A valid installer for OS %s is not available for this computer. "
|
||||||
"Run again without --os argument "
|
"Run again without --os argument "
|
||||||
"to select a valid build to download.\n" % args.os
|
"to select a valid build to download.\n" % args.os
|
||||||
)
|
)
|
||||||
@ -1254,7 +1307,7 @@ def main():
|
|||||||
except NameError:
|
except NameError:
|
||||||
print(
|
print(
|
||||||
"\n"
|
"\n"
|
||||||
"No valid version available. "
|
"No valid version available for this computer. "
|
||||||
"Run again without --auto argument "
|
"Run again without --auto argument "
|
||||||
"to select a valid build to download.\n"
|
"to select a valid build to download.\n"
|
||||||
)
|
)
|
||||||
@ -1344,7 +1397,9 @@ def main():
|
|||||||
% seeding_program
|
% seeding_program
|
||||||
)
|
)
|
||||||
xattr.setxattr(
|
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)
|
print("Product downloaded and installed to %s" % sparse_diskimage_path)
|
||||||
if args.raw:
|
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