Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
corpnewt
GitHub Repository: corpnewt/gibMacOS
Path: blob/master/BuildmacOSInstallApp.py
174 views
1
#!/usr/bin/env python
2
from Scripts import *
3
import os, datetime, shutil, time, sys, argparse
4
5
# Using the techniques outlined by wolfmannight here: https://www.insanelymac.com/forum/topic/338810-create-legit-copy-of-macos-from-apple-catalog/
6
7
class buildMacOSInstallApp:
8
def __init__(self):
9
self.r = run.Run()
10
self.u = utils.Utils("Build macOS Install App")
11
self.target_files = [
12
"BaseSystem.dmg",
13
"BaseSystem.chunklist",
14
"InstallESDDmg.pkg",
15
"InstallInfo.plist",
16
"AppleDiagnostics.dmg",
17
"AppleDiagnostics.chunklist"
18
]
19
# Verify we're on macOS - this doesn't work anywhere else
20
if not sys.platform == "darwin":
21
self.u.head("WARNING")
22
print("")
23
print("This script only runs on macOS!")
24
print("")
25
exit(1)
26
27
def mount_dmg(self, dmg, no_browse = False):
28
# Mounts the passed dmg and returns the mount point(s)
29
args = ["/usr/bin/hdiutil", "attach", dmg, "-plist", "-noverify"]
30
if no_browse:
31
args.append("-nobrowse")
32
out = self.r.run({"args":args})
33
if out[2] != 0:
34
# Failed!
35
raise Exception("Mount Failed!", "{} failed to mount:\n\n{}".format(os.path.basename(dmg), out[1]))
36
# Get the plist data returned, and locate the mount points
37
try:
38
plist_data = plist.loads(out[0])
39
mounts = [x["mount-point"] for x in plist_data.get("system-entities", []) if "mount-point" in x]
40
return mounts
41
except:
42
raise Exception("Mount Failed!", "No mount points returned from {}".format(os.path.basename(dmg)))
43
44
def unmount_dmg(self, mount_point):
45
# Unmounts the passed dmg or mount point - retries with force if failed
46
# Can take either a single point or a list
47
if not type(mount_point) is list:
48
mount_point = [mount_point]
49
unmounted = []
50
for m in mount_point:
51
args = ["/usr/bin/hdiutil", "detach", m]
52
out = self.r.run({"args":args})
53
if out[2] != 0:
54
# Polite failed, let's crush this b!
55
args.append("-force")
56
out = self.r.run({"args":args})
57
if out[2] != 0:
58
# Oh... failed again... onto the next...
59
print(out[1])
60
continue
61
unmounted.append(m)
62
return unmounted
63
64
def main(self):
65
while True:
66
self.u.head()
67
print("")
68
print("Q. Quit")
69
print("")
70
fold = self.u.grab("Please drag and drop the output folder from gibMacOS here: ")
71
print("")
72
if fold.lower() == "q":
73
self.u.custom_quit()
74
f_path = self.u.check_path(fold)
75
if not f_path:
76
print("That path does not exist!\n")
77
self.u.grab("Press [enter] to return...")
78
continue
79
# Let's check if it's a folder. If not, make the next directory up the target
80
if not os.path.isdir(f_path):
81
f_path = os.path.dirname(os.path.realpath(f_path))
82
# Walk the contents of f_path and ensure we have all the needed files
83
lower_contents = [y.lower() for y in os.listdir(f_path)]
84
# Check if we got an InstallAssistant.pkg - and if so, just open that
85
if "installassistant.pkg" in lower_contents:
86
self.u.head("InstallAssistant.pkg Found")
87
print("")
88
print("Located InstallAssistant.pkg in the passed folder.\n")
89
print("As of macOS Big Sur (11.x), Apple changed how they distribute the OS files in")
90
print("the software update catalog.\n")
91
print("Double clicking the InstallAssistant.pkg will open it in Installer, which will")
92
print("copy the Install macOS [version].app to your /Applications folder.\n")
93
print("Opening InstallAssistant.pkg...")
94
self.r.run({"args":["open",os.path.join(f_path,"InstallAssistant.pkg")]})
95
print("")
96
self.u.grab("Press [enter] to return...")
97
continue
98
missing_list = [x for x in self.target_files if not x.lower() in lower_contents]
99
if len(missing_list):
100
self.u.head("Missing Required Files")
101
print("")
102
print("That folder is missing the following required files:")
103
print(", ".join(missing_list))
104
print("")
105
self.u.grab("Press [enter] to return...")
106
# Time to build the installer!
107
cwd = os.getcwd()
108
os.chdir(f_path)
109
base_mounts = []
110
try:
111
self.u.head("Building Installer")
112
print("")
113
print("Taking ownership of downloaded files...")
114
for x in self.target_files:
115
print(" - {}...".format(x))
116
self.r.run({"args":["chmod","a+x",x]})
117
print("Mounting BaseSystem.dmg...")
118
base_mounts = self.mount_dmg("BaseSystem.dmg")
119
if not len(base_mounts):
120
raise Exception("Mount Failed!", "No mount points were returned from BaseSystem.dmg")
121
base_mount = base_mounts[0] # Let's assume the first
122
print("Locating Installer app...")
123
install_app = next((x for x in os.listdir(base_mount) if os.path.isdir(os.path.join(base_mount,x)) and x.lower().endswith(".app") and not x.startswith(".")),None)
124
if not install_app:
125
raise Exception("Installer app not located in {}".format(base_mount))
126
print(" - Found {}".format(install_app))
127
# Copy the .app over
128
out = self.r.run({"args":["cp","-R",os.path.join(base_mount,install_app),os.path.join(f_path,install_app)]})
129
if out[2] != 0:
130
raise Exception("Copy Failed!", out[1])
131
print("Unmounting BaseSystem.dmg...")
132
for x in base_mounts:
133
self.unmount_dmg(x)
134
base_mounts = []
135
shared_support = os.path.join(f_path,install_app,"Contents","SharedSupport")
136
if not os.path.exists(shared_support):
137
print("Creating SharedSupport directory...")
138
os.makedirs(shared_support)
139
print("Copying files to SharedSupport...")
140
for x in self.target_files:
141
y = "InstallESD.dmg" if x.lower() == "installesddmg.pkg" else x # InstallESDDmg.pkg gets renamed to InstallESD.dmg - all others stay the same
142
print(" - {}{}".format(x, " --> {}".format(y) if y != x else ""))
143
out = self.r.run({"args":["cp","-R",os.path.join(f_path,x),os.path.join(shared_support,y)]})
144
if out[2] != 0:
145
raise Exception("Copy Failed!", out[1])
146
print("Patching InstallInfo.plist...")
147
with open(os.path.join(shared_support,"InstallInfo.plist"),"rb") as f:
148
p = plist.load(f)
149
if "Payload Image Info" in p:
150
pii = p["Payload Image Info"]
151
if "URL" in pii: pii["URL"] = pii["URL"].replace("InstallESDDmg.pkg","InstallESD.dmg")
152
if "id" in pii: pii["id"] = pii["id"].replace("com.apple.pkg.InstallESDDmg","com.apple.dmg.InstallESD")
153
pii.pop("chunklistURL",None)
154
pii.pop("chunklistid",None)
155
with open(os.path.join(shared_support,"InstallInfo.plist"),"wb") as f:
156
plist.dump(p,f)
157
print("")
158
print("Created: {}".format(install_app))
159
print("Saved to: {}".format(os.path.join(f_path,install_app)))
160
print("")
161
self.u.grab("Press [enter] to return...")
162
except Exception as e:
163
print("An error occurred:")
164
print(" - {}".format(e))
165
print("")
166
if len(base_mounts):
167
for x in base_mounts:
168
print(" - Unmounting {}...".format(x))
169
self.unmount_dmg(x)
170
print("")
171
self.u.grab("Press [enter] to return...")
172
173
if __name__ == '__main__':
174
b = buildMacOSInstallApp()
175
b.main()
176
177