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