mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-13 20:28:21 +10:00
Merge pull request #1133 from dortania/pkg-support
Implement Package and Privileged Helper Tool support
This commit is contained in:
119
.github/workflows/build-app-wxpython.yml
vendored
119
.github/workflows/build-app-wxpython.yml
vendored
@@ -13,44 +13,85 @@ jobs:
|
||||
if: github.repository_owner == 'dortania'
|
||||
|
||||
env:
|
||||
# GitHub Information
|
||||
branch: ${{ github.ref }}
|
||||
commiturl: ${{ github.event.head_commit.url }}${{ github.event.release.html_url }}
|
||||
commitdate: ${{ github.event.head_commit.timestamp }}${{ github.event.release.published_at }}
|
||||
MAC_CODESIGN_IDENTITY: ${{ secrets.MAC_CODESIGN_IDENTITY }}
|
||||
MAC_CODESIGN_CERT: ${{ secrets.MAC_CODESIGN_CERT }}
|
||||
MAC_NOTARIZATION_USERNAME: ${{ secrets.MAC_NOTARIZATION_USERNAME }}
|
||||
MAC_NOTARIZATION_PASSWORD: ${{ secrets.MAC_NOTARIZATION_PASSWORD }}
|
||||
MAC_NOTARIZATION_TEAM_ID: ${{ secrets.MAC_NOTARIZATION_TEAM_ID }}
|
||||
|
||||
# Analytics
|
||||
ANALYTICS_KEY: ${{ secrets.ANALYTICS_KEY }}
|
||||
ANALYTICS_SITE: ${{ secrets.ANALYTICS_SITE }}
|
||||
|
||||
# App Signing
|
||||
ORG_MAC_DEVELOPER_ID_APPLICATION_IDENTITY: ${{ secrets.ORG_MAC_DEVELOPER_ID_APPLICATION_IDENTITY }}
|
||||
|
||||
# PKG Signing
|
||||
ORG_MAC_DEVELOPER_ID_INSTALLER_IDENTITY: ${{ secrets.ORG_MAC_DEVELOPER_ID_INSTALLER_IDENTITY }}
|
||||
|
||||
# Notarization
|
||||
ORG_MAC_NOTARIZATION_TEAM_ID: ${{ secrets.ORG_MAC_NOTARIZATION_TEAM_ID }}
|
||||
ORG_MAC_NOTARIZATION_APPLE_ID: ${{ secrets.ORG_MAC_NOTARIZATION_APPLE_ID }}
|
||||
ORG_MAC_NOTARIZATION_PASSWORD: ${{ secrets.ORG_MAC_NOTARIZATION_PASSWORD }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build Binary
|
||||
run: /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 Build-Binary.command --reset_binaries --branch "${{ env.branch }}" --commit "${{ env.commiturl }}" --commit_date "${{ env.commitdate }}" --key "${{ env.ANALYTICS_KEY }}" --site "${{ env.ANALYTICS_SITE }}"
|
||||
|
||||
# - name: Import Certificate
|
||||
# if: (!security find-certificate -c "${{ env.MAC_CODESIGN_IDENTITY }}")
|
||||
# uses: apple-actions/import-codesign-certs@v2
|
||||
# - name: Import Application Signing Certificate
|
||||
# uses: dhinakg/import-codesign-certs@master
|
||||
# with:
|
||||
# p12-file-base64: ${{ secrets.MAC_CODESIGN_CERT }}
|
||||
# p12-password: ${{ secrets.MAC_NOTARIZATION_PASSWORD }}
|
||||
# p12-file-base64: ${{ secrets.ORG_MAC_DEVELOPER_ID_APPLICATION_CERT_P12_BASE64 }}
|
||||
# p12-password: ${{ secrets.ORG_MAC_DEVELOPER_ID_APPLICATION_CERT_P12_PASSWORD }}
|
||||
|
||||
- name: Codesign Binary
|
||||
run: 'codesign -s "${{ env.MAC_CODESIGN_IDENTITY }}" -v --force --deep --timestamp --entitlements ./ci_tooling/entitlements/entitlements.plist -o runtime "dist/OpenCore-Patcher.app"'
|
||||
# - name: Import Installer Signing Certificate
|
||||
# uses: dhinakg/import-codesign-certs@master
|
||||
# with:
|
||||
# p12-file-base64: ${{ secrets.ORG_MAC_DEVELOPER_ID_INSTALLER_CERT_P12_BASE64 }}
|
||||
# p12-password: ${{ secrets.ORG_MAC_DEVELOPER_ID_INSTALLER_CERT_P12_PASSWORD }}
|
||||
|
||||
- name: Package Binary
|
||||
run: cd dist; ditto -c -k --sequesterRsrc --keepParent OpenCore-Patcher.app ../OpenCore-Patcher-wxPython.app.zip
|
||||
# - name: Install Dependencies
|
||||
# run: /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 -m pip install -r requirements.txt
|
||||
|
||||
- name: Notarize Binary
|
||||
run: xcrun notarytool submit OpenCore-Patcher-wxPython.app.zip --apple-id "${{ env.MAC_NOTARIZATION_USERNAME }}" --password "${{ env.MAC_NOTARIZATION_PASSWORD }}" --team-id "${{ env.MAC_NOTARIZATION_TEAM_ID }}"
|
||||
# - name: Force Universal2 charset for Python
|
||||
# run: |
|
||||
# /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 -m pip uninstall -y charset_normalizer
|
||||
# /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 -m pip download --platform macosx_10_9_universal2 --only-binary=:all: charset-normalizer
|
||||
# /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 -m pip install charset_normalizer-*-macosx_10_9_universal2.whl
|
||||
|
||||
- name: Generate support package
|
||||
run: /usr/local/bin/packagesbuild ./ci_tooling/autopkg/AutoPkg-Assets-Setup.pkgproj
|
||||
- name: Prepare Assets (--prepare-assets)
|
||||
run: >
|
||||
/Library/Frameworks/Python.framework/Versions/3.11/bin/python3 Build-Project.command
|
||||
--run-as-individual-steps --reset-dmg-cache
|
||||
--prepare-assets
|
||||
|
||||
- name: Prepare Application (--prepare-application)
|
||||
run: >
|
||||
/Library/Frameworks/Python.framework/Versions/3.11/bin/python3 Build-Project.command
|
||||
--application-signing-identity "${{ env.ORG_MAC_DEVELOPER_ID_APPLICATION_IDENTITY }}"
|
||||
--notarization-apple-id "${{ env.ORG_MAC_NOTARIZATION_APPLE_ID }}" --notarization-password "${{ env.ORG_MAC_NOTARIZATION_PASSWORD }}" --notarization-team-id "${{ env.ORG_MAC_NOTARIZATION_TEAM_ID }}"
|
||||
--git-branch "${{ env.branch }}" --git-commit-url "${{ env.commiturl }}" --git-commit-date "${{ env.commitdate }}"
|
||||
--analytics-key "${{ env.ANALYTICS_KEY }}" --analytics-endpoint "${{ env.ANALYTICS_SITE }}"
|
||||
--reset-pyinstaller-cache
|
||||
--run-as-individual-steps
|
||||
--prepare-application
|
||||
|
||||
- name: Prepare Package (--prepare-package)
|
||||
run: >
|
||||
/Library/Frameworks/Python.framework/Versions/3.11/bin/python3 Build-Project.command
|
||||
--installer-signing-identity "${{ env.ORG_MAC_DEVELOPER_ID_INSTALLER_IDENTITY }}"
|
||||
--notarization-apple-id "${{ env.ORG_MAC_NOTARIZATION_APPLE_ID }}" --notarization-password "${{ env.ORG_MAC_NOTARIZATION_PASSWORD }}" --notarization-team-id "${{ env.ORG_MAC_NOTARIZATION_TEAM_ID }}"
|
||||
--run-as-individual-steps
|
||||
--prepare-package
|
||||
|
||||
- name: Prepare Update Shim (--prepare-shim)
|
||||
run: >
|
||||
/Library/Frameworks/Python.framework/Versions/3.11/bin/python3 Build-Project.command
|
||||
--application-signing-identity "${{ env.ORG_MAC_DEVELOPER_ID_APPLICATION_IDENTITY }}"
|
||||
--notarization-apple-id "${{ env.ORG_MAC_NOTARIZATION_APPLE_ID }}" --notarization-password "${{ env.ORG_MAC_NOTARIZATION_PASSWORD }}" --notarization-team-id "${{ env.ORG_MAC_NOTARIZATION_TEAM_ID }}"
|
||||
--run-as-individual-steps
|
||||
--prepare-shim
|
||||
|
||||
- name: Prepare App for Upload
|
||||
run: mv ./OpenCore-Patcher-wxPython.app.zip ./OpenCore-Patcher-GUI.app.zip
|
||||
run: /bin/mv ./dist/OpenCore-Patcher.app.zip ./OpenCore-Patcher-GUI.app.zip
|
||||
|
||||
- name: Upload App to Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -58,12 +99,24 @@ jobs:
|
||||
name: OpenCore-Patcher.app (GUI)
|
||||
path: OpenCore-Patcher-GUI.app.zip
|
||||
|
||||
- name: Upload Package to Artifacts
|
||||
- name: Upload AutoPkg Package to Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AutoPkg-Assets.pkg
|
||||
path: ./dist/AutoPkg-Assets.pkg
|
||||
|
||||
- name: Upload Installation Package to Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: OpenCore-Patcher.pkg
|
||||
path: ./dist/OpenCore-Patcher.pkg
|
||||
|
||||
- name: Upload Uninstaller Package to Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: OpenCore-Patcher-Uninstaller.pkg
|
||||
path: ./dist/OpenCore-Patcher-Uninstaller.pkg
|
||||
|
||||
- name: Upload Binary to Release
|
||||
if: github.event_name == 'release'
|
||||
uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d
|
||||
@@ -73,11 +126,29 @@ jobs:
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
|
||||
- name: Upload Package to Release
|
||||
- name: Upload AutoPkg Package to Release
|
||||
if: github.event_name == 'release'
|
||||
uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./dist/AutoPkg-Assets.pkg
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
|
||||
- name: Upload Installation Package to Release
|
||||
if: github.event_name == 'release'
|
||||
uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./dist/OpenCore-Patcher.pkg
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
|
||||
- name: Upload Uninstaller Package to Release
|
||||
if: github.event_name == 'release'
|
||||
uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./dist/OpenCore-Patcher-Uninstaller.pkg
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,3 +39,4 @@ __pycache__/
|
||||
/payloads/update.sh
|
||||
/payloads/OpenCore-Patcher.app
|
||||
/.x86_64_venv
|
||||
*afdesign~lock~
|
||||
|
||||
@@ -1,433 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Build-Binary.command: Generate stand alone application for OpenCore-Patcher
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
import plistlib
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from opencore_legacy_patcher import constants
|
||||
|
||||
|
||||
class CreateBinary:
|
||||
"""
|
||||
Library for creating OpenCore-Patcher application
|
||||
|
||||
This script's main purpose is to handle the following:
|
||||
- Download external dependencies (ex. PatcherSupportPkg)
|
||||
- Convert payloads directory into DMG
|
||||
- Build Binary via Pyinstaller
|
||||
- Patch 'LC_VERSION_MIN_MACOSX' to OS X 10.10
|
||||
- Add commit data to Info.plist
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
start = time.time()
|
||||
self._set_cwd()
|
||||
|
||||
print("Starting build script")
|
||||
self.args = self._parse_arguments()
|
||||
|
||||
print(f"Current Working Directory:\n- {os.getcwd()}")
|
||||
|
||||
self._preflight_processes()
|
||||
self._build_binary()
|
||||
self._postflight_processes()
|
||||
print(f"Build script completed in {str(round(time.time() - start, 2))} seconds")
|
||||
|
||||
|
||||
def _set_cwd(self):
|
||||
"""
|
||||
Initialize current working directory to parent of this script
|
||||
"""
|
||||
|
||||
os.chdir(Path(__file__).resolve().parent)
|
||||
|
||||
|
||||
def _parse_arguments(self):
|
||||
"""
|
||||
Parse arguments passed to script
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description='Builds OpenCore-Patcher binary')
|
||||
parser.add_argument('--branch', type=str, help='Git branch name')
|
||||
parser.add_argument('--commit', type=str, help='Git commit URL')
|
||||
parser.add_argument('--commit_date', type=str, help='Git commit date')
|
||||
parser.add_argument('--reset_binaries', action='store_true', help='Force redownload and imaging of payloads')
|
||||
parser.add_argument('--key', type=str, help='Developer key for signing')
|
||||
parser.add_argument('--site', type=str, help='Path to server')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def _setup_pathing(self):
|
||||
"""
|
||||
Initialize pathing for pyinstaller
|
||||
"""
|
||||
|
||||
python_path = sys.executable
|
||||
python_binary = python_path.split("/")[-1]
|
||||
python_bin_dir = python_path.strip(python_binary)
|
||||
|
||||
# macOS (using Python installed by homebrew (e.g. brew))
|
||||
if f"/usr/local/opt/python@3." in sys.executable:
|
||||
print(f"\t* NOTE: home(brew) python3 detected; using (sys.exec_prefix, python_path) ==> {sys.exec_prefix, python_path}")
|
||||
# - under brew, pip3 will install pyinstaller at:
|
||||
# /usr/local/lib/python3.9/site-packages/pyinstaller
|
||||
# and /usr/local/bin/pyinstaller stub to load and run.
|
||||
|
||||
pyinstaller_path = f"/usr/local/bin/pyinstaller"
|
||||
else:
|
||||
pyinstaller_path = f"{python_bin_dir}pyinstaller"
|
||||
|
||||
if not Path(pyinstaller_path).exists():
|
||||
print(f"- pyinstaller not found:\n\t{pyinstaller_path}")
|
||||
raise Exception("pyinstaller not found")
|
||||
|
||||
self.pyinstaller_path = pyinstaller_path
|
||||
|
||||
|
||||
def _preflight_processes(self):
|
||||
"""
|
||||
Start preflight processes
|
||||
"""
|
||||
|
||||
print("Starting preflight processes")
|
||||
self._setup_pathing()
|
||||
self._delete_extra_binaries()
|
||||
self._download_resources()
|
||||
self._generate_payloads_dmg()
|
||||
|
||||
|
||||
def _postflight_processes(self):
|
||||
"""
|
||||
Start postflight processes
|
||||
"""
|
||||
|
||||
print("Starting postflight processes")
|
||||
self._patch_load_command()
|
||||
self._add_commit_data()
|
||||
self._post_flight_cleanup()
|
||||
self._mini_validate()
|
||||
|
||||
|
||||
def _build_binary(self):
|
||||
"""
|
||||
Build binary via pyinstaller
|
||||
"""
|
||||
|
||||
if Path(f"./dist/OpenCore-Patcher.app").exists():
|
||||
print("Found OpenCore-Patcher.app, removing...")
|
||||
rm_output = subprocess.run(
|
||||
["/bin/rm", "-rf", "./dist/OpenCore-Patcher.app"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
if rm_output.returncode != 0:
|
||||
print("Remove failed")
|
||||
print(rm_output.stderr.decode('utf-8'))
|
||||
raise Exception("Remove failed")
|
||||
|
||||
self._embed_key()
|
||||
|
||||
print("Building GUI binary...")
|
||||
build_args = [self.pyinstaller_path, "./OpenCore-Patcher-GUI.spec", "--noconfirm"]
|
||||
|
||||
build_result = subprocess.run(build_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
self._strip_key()
|
||||
|
||||
if build_result.returncode != 0:
|
||||
print("Build failed")
|
||||
print(build_result.stderr.decode('utf-8'))
|
||||
raise Exception("Build failed")
|
||||
|
||||
# Next embed support icns into ./Resources
|
||||
print("Embedding icns...")
|
||||
for file in Path("payloads/Icon/AppIcons").glob("*.icns"):
|
||||
subprocess.run(
|
||||
["/bin/cp", str(file), "./dist/OpenCore-Patcher.app/Contents/Resources/"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
|
||||
def _embed_key(self):
|
||||
"""
|
||||
Embed developer key into binary
|
||||
"""
|
||||
|
||||
if not self.args.key:
|
||||
print("No developer key provided, skipping...")
|
||||
return
|
||||
if not self.args.site:
|
||||
print("No site provided, skipping...")
|
||||
return
|
||||
|
||||
print("Embedding developer key...")
|
||||
if not Path("./resources/analytics_handler.py").exists():
|
||||
print("analytics_handler.py not found")
|
||||
return
|
||||
|
||||
lines = []
|
||||
with open("./resources/analytics_handler.py", "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("SITE_KEY: str = "):
|
||||
lines[i] = f"SITE_KEY: str = \"{self.args.key}\"\n"
|
||||
elif line.startswith("ANALYTICS_SERVER: str = "):
|
||||
lines[i] = f"ANALYTICS_SERVER: str = \"{self.args.site}\"\n"
|
||||
|
||||
with open("./resources/analytics_handler.py", "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
||||
def _strip_key(self):
|
||||
"""
|
||||
Strip developer key from binary
|
||||
"""
|
||||
|
||||
if not self.args.key:
|
||||
print("No developer key provided, skipping...")
|
||||
return
|
||||
if not self.args.site:
|
||||
print("No site provided, skipping...")
|
||||
return
|
||||
|
||||
print("Stripping developer key...")
|
||||
if not Path("./resources/analytics_handler.py").exists():
|
||||
print("analytics_handler.py not found")
|
||||
return
|
||||
|
||||
lines = []
|
||||
with open("./resources/analytics_handler.py", "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("SITE_KEY: str = "):
|
||||
lines[i] = f"SITE_KEY: str = \"\"\n"
|
||||
elif line.startswith("ANALYTICS_SERVER: str = "):
|
||||
lines[i] = f"ANALYTICS_SERVER: str = \"\"\n"
|
||||
|
||||
with open("./resources/analytics_handler.py", "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
||||
def _delete_extra_binaries(self):
|
||||
"""
|
||||
Delete extra binaries from payloads directory
|
||||
"""
|
||||
|
||||
whitelist_folders = [
|
||||
"ACPI",
|
||||
"Config",
|
||||
"Drivers",
|
||||
"Icon",
|
||||
"Kexts",
|
||||
"OpenCore",
|
||||
"Tools",
|
||||
"Launch Services",
|
||||
]
|
||||
|
||||
whitelist_files = [
|
||||
|
||||
]
|
||||
|
||||
|
||||
print("Deleting extra binaries...")
|
||||
for file in Path("payloads").glob(pattern="*"):
|
||||
if file.is_dir():
|
||||
if file.name in whitelist_folders:
|
||||
continue
|
||||
print(f"- Deleting {file.name}")
|
||||
subprocess.run(["/bin/rm", "-rf", file])
|
||||
else:
|
||||
if file.name in whitelist_files:
|
||||
continue
|
||||
print(f"- Deleting {file.name}")
|
||||
subprocess.run(["/bin/rm", "-f", file])
|
||||
|
||||
|
||||
def _download_resources(self):
|
||||
"""
|
||||
Download required dependencies
|
||||
"""
|
||||
|
||||
patcher_support_pkg_version = constants.Constants().patcher_support_pkg_version
|
||||
required_resources = [
|
||||
"Universal-Binaries.dmg"
|
||||
]
|
||||
|
||||
print("Downloading required resources...")
|
||||
for resource in required_resources:
|
||||
if Path(f"./{resource}").exists():
|
||||
if self.args.reset_binaries:
|
||||
print(f" - Removing old {resource}")
|
||||
# Just to be safe
|
||||
assert resource, "Resource cannot be empty"
|
||||
assert resource not in ("/", "."), "Resource cannot be root"
|
||||
rm_output = subprocess.run(
|
||||
["/bin/rm", "-rf", f"./{resource}"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
if rm_output.returncode != 0:
|
||||
print("Remove failed")
|
||||
print(rm_output.stderr.decode('utf-8'))
|
||||
raise Exception("Remove failed")
|
||||
else:
|
||||
print(f"- {resource} already exists, skipping download")
|
||||
continue
|
||||
print(f"- Downloading {resource}...")
|
||||
|
||||
download_result = subprocess.run(
|
||||
[
|
||||
"/usr/bin/curl", "-LO",
|
||||
f"https://github.com/dortania/PatcherSupportPkg/releases/download/{patcher_support_pkg_version}/{resource}"
|
||||
],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
if download_result.returncode != 0:
|
||||
print("- Download failed")
|
||||
print(download_result.stderr.decode('utf-8'))
|
||||
raise Exception("Download failed")
|
||||
if not Path(f"./{resource}").exists():
|
||||
print(f"- {resource} not found")
|
||||
raise Exception(f"{resource} not found")
|
||||
|
||||
|
||||
def _generate_payloads_dmg(self):
|
||||
"""
|
||||
Generate disk image containing all payloads
|
||||
Disk image will be password protected due to issues with
|
||||
Apple's notarization system and inclusion of kernel extensions
|
||||
"""
|
||||
|
||||
if Path("./payloads.dmg").exists():
|
||||
if not self.args.reset_binaries:
|
||||
print("- payloads.dmg already exists, skipping creation")
|
||||
return
|
||||
|
||||
print("- Removing old payloads.dmg")
|
||||
rm_output = subprocess.run(
|
||||
["/bin/rm", "-rf", "./payloads.dmg"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
if rm_output.returncode != 0:
|
||||
print("Remove failed")
|
||||
print(rm_output.stderr.decode('utf-8'))
|
||||
raise Exception("Remove failed")
|
||||
|
||||
print("- Generating DMG...")
|
||||
dmg_output = subprocess.run([
|
||||
'/usr/bin/hdiutil', 'create', './payloads.dmg',
|
||||
'-megabytes', '32000', # Overlays can only be as large as the disk image allows
|
||||
'-format', 'UDZO', '-ov',
|
||||
'-volname', 'OpenCore Patcher Resources (Base)',
|
||||
'-fs', 'HFS+',
|
||||
'-srcfolder', './payloads',
|
||||
'-passphrase', 'password', '-encryption'
|
||||
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if dmg_output.returncode != 0:
|
||||
print("- DMG generation failed")
|
||||
print(dmg_output.stderr.decode('utf-8'))
|
||||
raise Exception("DMG generation failed")
|
||||
|
||||
print("- DMG generation complete")
|
||||
|
||||
|
||||
def _add_commit_data(self):
|
||||
"""
|
||||
Add commit data to Info.plist
|
||||
"""
|
||||
|
||||
if not self.args.branch and not self.args.commit and not self.args.commit_date:
|
||||
print("- No commit data provided, adding source info")
|
||||
branch = "Built from source"
|
||||
commit_url = ""
|
||||
commit_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
else:
|
||||
branch = self.args.branch
|
||||
commit_url = self.args.commit
|
||||
commit_date = self.args.commit_date
|
||||
print("- Adding commit data to Info.plist")
|
||||
plist_path = Path("./dist/OpenCore-Patcher.app/Contents/Info.plist")
|
||||
plist = plistlib.load(Path(plist_path).open("rb"))
|
||||
plist["Github"] = {
|
||||
"Branch": branch,
|
||||
"Commit URL": commit_url,
|
||||
"Commit Date": commit_date,
|
||||
}
|
||||
plistlib.dump(plist, Path(plist_path).open("wb"), sort_keys=True)
|
||||
|
||||
|
||||
def _patch_load_command(self):
|
||||
"""
|
||||
Patch LC_VERSION_MIN_MACOSX in Load Command to report 10.10
|
||||
|
||||
By default Pyinstaller will create binaries supporting 10.13+
|
||||
However this limitation is entirely arbitrary for our libraries
|
||||
and instead we're able to support 10.10 without issues.
|
||||
|
||||
To verify set version:
|
||||
otool -l ./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher
|
||||
|
||||
cmd LC_VERSION_MIN_MACOSX
|
||||
cmdsize 16
|
||||
version 10.13
|
||||
sdk 10.9
|
||||
|
||||
"""
|
||||
|
||||
print("- Patching LC_VERSION_MIN_MACOSX")
|
||||
path = './dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher'
|
||||
find = b'\x00\x0D\x0A\x00' # 10.13 (0xA0D)
|
||||
replace = b'\x00\x0A\x0A\x00' # 10.10 (0xA0A)
|
||||
with open(path, 'rb') as f:
|
||||
data = f.read()
|
||||
data = data.replace(find, replace, 1)
|
||||
with open(path, 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
def _post_flight_cleanup(self):
|
||||
"""
|
||||
Post flight cleanup
|
||||
"""
|
||||
|
||||
path = "./dist/OpenCore-Patcher"
|
||||
print(f"- Removing {path}")
|
||||
rm_output = subprocess.run(
|
||||
["/bin/rm", "-rf", path],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
if rm_output.returncode != 0:
|
||||
print(f"- Remove failed: {path}")
|
||||
print(rm_output.stderr.decode('utf-8'))
|
||||
raise Exception(f"Remove failed: {path}")
|
||||
|
||||
|
||||
def _mini_validate(self):
|
||||
"""
|
||||
Validate generated binary
|
||||
"""
|
||||
|
||||
print("- Validating binary")
|
||||
validate_output = subprocess.run(
|
||||
["./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher", "--build", "--model", "MacPro3,1"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
if validate_output.returncode != 0:
|
||||
print("- Validation failed")
|
||||
print(validate_output.stderr.decode('utf-8'))
|
||||
raise Exception("Validation failed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
CreateBinary()
|
||||
168
Build-Project.command
Normal file
168
Build-Project.command
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Build-Project.command: Generate OpenCore-Patcher.app and OpenCore-Patcher.pkg
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
import plistlib
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from ci_tooling.build_modules import (
|
||||
application,
|
||||
disk_images,
|
||||
package,
|
||||
sign_notarize,
|
||||
shim
|
||||
)
|
||||
|
||||
from opencore_legacy_patcher import constants
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Parse Command Line Arguments
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description="Build OpenCore Legacy Patcher Suite", add_help=False)
|
||||
|
||||
# Signing Parameters
|
||||
parser.add_argument("--application-signing-identity", type=str, help="Application Signing Identity")
|
||||
parser.add_argument("--installer-signing-identity", type=str, help="Installer Signing Identity")
|
||||
|
||||
|
||||
# Notarization Parameters
|
||||
parser.add_argument("--notarization-apple-id", type=str, help="Notarization Apple ID", default=None)
|
||||
parser.add_argument("--notarization-password", type=str, help="Notarization Password", default=None)
|
||||
parser.add_argument("--notarization-team-id", type=str, help="Notarization Team ID", default=None)
|
||||
|
||||
# GitHub Actions CI/CD Parameters
|
||||
parser.add_argument("--git-branch", type=str, help="Git Branch", default=None)
|
||||
parser.add_argument("--git-commit-url", type=str, help="Git Commit URL", default=None)
|
||||
parser.add_argument("--git-commit-date", type=str, help="Git Commit Date", default=None)
|
||||
|
||||
# Local Build Parameters
|
||||
parser.add_argument("--reset-dmg-cache", action="store_true", help="Redownload PatcherSupportPkg.dmg and regenerate payloads.dmg", default=False)
|
||||
parser.add_argument("--reset-pyinstaller-cache", action="store_true", help="Clean PyInstaller Cache", default=False)
|
||||
|
||||
# CI/CD Parameters for individual steps
|
||||
# If not specified, will run all steps
|
||||
parser.add_argument("--run-as-individual-steps", action="store_true", help="CI: Run as individual steps", default=False)
|
||||
parser.add_argument("--prepare-application", action="store_true", help="CI: Prepare Application", default=False)
|
||||
parser.add_argument("--prepare-package", action="store_true", help="CI: Prepare Package", default=False)
|
||||
|
||||
# CI/CD Parameters for additional steps
|
||||
# If not specified, will not run additional steps
|
||||
parser.add_argument("--prepare-assets", action="store_true", help="CI: Prepare Assets", default=False)
|
||||
parser.add_argument("--prepare-shim", action="store_true", help="CI: Prepare Update Shim", default=False)
|
||||
|
||||
# Analytics Parameters
|
||||
parser.add_argument("--analytics-key", type=str, help="Analytics Key", default=None)
|
||||
parser.add_argument("--analytics-endpoint", type=str, help="Analytics Endpoint", default=None)
|
||||
|
||||
# Help
|
||||
parser.add_argument("--help", action="store_true", help="Show this help message and exit", default=False)
|
||||
|
||||
# Parse Arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.help:
|
||||
parser.print_help()
|
||||
print("\n\nIf running outside of CI/CD, simply run the following command:")
|
||||
print("$ python3 Build-Project.command")
|
||||
sys.exit(0)
|
||||
|
||||
# Set 'Current Working Directory' to script directory
|
||||
os.chdir(Path(__file__).resolve().parent)
|
||||
|
||||
|
||||
if (args.prepare_assets):
|
||||
# Prepare workspace
|
||||
disk_images.GenerateDiskImages(args.reset_dmg_cache).generate()
|
||||
|
||||
if (args.run_as_individual_steps is False) or (args.run_as_individual_steps and args.prepare_application):
|
||||
# Prepare Privileged Helper Tool
|
||||
sign_notarize.SignAndNotarize(
|
||||
path=Path("./ci_tooling/privileged_helper_tool/com.dortania.opencore-legacy-patcher.privileged-helper"),
|
||||
signing_identity=args.application_signing_identity,
|
||||
notarization_apple_id=args.notarization_apple_id,
|
||||
notarization_password=args.notarization_password,
|
||||
notarization_team_id=args.notarization_team_id,
|
||||
).sign_and_notarize()
|
||||
|
||||
# Build OpenCore-Patcher.app
|
||||
application.GenerateApplication(
|
||||
reset_pyinstaller_cache=args.reset_pyinstaller_cache,
|
||||
git_branch=args.git_branch,
|
||||
git_commit_url=args.git_commit_url,
|
||||
git_commit_date=args.git_commit_date,
|
||||
analytics_key=args.analytics_key,
|
||||
analytics_endpoint=args.analytics_endpoint,
|
||||
).generate()
|
||||
|
||||
# Sign OpenCore-Patcher.app
|
||||
sign_notarize.SignAndNotarize(
|
||||
path=Path("dist/OpenCore-Patcher.app"),
|
||||
signing_identity=args.application_signing_identity,
|
||||
notarization_apple_id=args.notarization_apple_id,
|
||||
notarization_password=args.notarization_password,
|
||||
notarization_team_id=args.notarization_team_id,
|
||||
entitlements=Path("./ci_tooling/entitlements/entitlements.plist"),
|
||||
).sign_and_notarize()
|
||||
|
||||
|
||||
if (args.run_as_individual_steps is False) or (args.run_as_individual_steps and args.prepare_package):
|
||||
# Build OpenCore-Patcher.pkg and OpenCore-Patcher-Uninstaller.pkg
|
||||
package.GeneratePackage().generate()
|
||||
|
||||
# Sign OpenCore-Patcher.pkg
|
||||
sign_notarize.SignAndNotarize(
|
||||
path=Path("dist/OpenCore-Patcher.pkg"),
|
||||
signing_identity=args.installer_signing_identity,
|
||||
notarization_apple_id=args.notarization_apple_id,
|
||||
notarization_password=args.notarization_password,
|
||||
notarization_team_id=args.notarization_team_id,
|
||||
).sign_and_notarize()
|
||||
|
||||
# Sign OpenCore-Patcher-Uninstaller.pkg
|
||||
sign_notarize.SignAndNotarize(
|
||||
path=Path("dist/OpenCore-Patcher-Uninstaller.pkg"),
|
||||
signing_identity=args.installer_signing_identity,
|
||||
notarization_apple_id=args.notarization_apple_id,
|
||||
notarization_password=args.notarization_password,
|
||||
notarization_team_id=args.notarization_team_id,
|
||||
).sign_and_notarize()
|
||||
|
||||
# Create Update Shim
|
||||
if args.prepare_shim:
|
||||
shim.GenerateShim().generate()
|
||||
if Path("dist/OpenCore-Patcher.app").exists():
|
||||
if Path("dist/OpenCore-Patcher (Original).app").exists():
|
||||
Path("dist/OpenCore-Patcher (Original).app").unlink()
|
||||
Path("dist/OpenCore-Patcher.app").rename("dist/OpenCore-Patcher (Original).app")
|
||||
Path("dist/OpenCore-Patcher (Shim).app").rename("dist/OpenCore-Patcher.app")
|
||||
|
||||
# Update app version in Info.plist
|
||||
plist_path = Path("dist/OpenCore-Patcher.app/Contents/Info.plist")
|
||||
contents = plistlib.load(plist_path.open("rb"))
|
||||
contents["CFBundleVersion"] = constants.Constants().patcher_version
|
||||
contents["CFBundleShortVersionString"] = constants.Constants().patcher_version
|
||||
plistlib.dump(contents, plist_path.open("wb"))
|
||||
|
||||
sign_notarize.SignAndNotarize(
|
||||
path=Path("dist/OpenCore-Patcher.app"),
|
||||
signing_identity=args.application_signing_identity,
|
||||
notarization_apple_id=args.notarization_apple_id,
|
||||
notarization_password=args.notarization_password,
|
||||
notarization_team_id=args.notarization_team_id,
|
||||
entitlements=Path("./ci_tooling/entitlements/entitlements.plist"),
|
||||
).sign_and_notarize()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
_start = time.time()
|
||||
main()
|
||||
print(f"Build script completed in {str(round(time.time() - _start, 2))} seconds")
|
||||
@@ -74,6 +74,7 @@ app = BUNDLE(coll,
|
||||
bundle_identifier="com.dortania.opencore-legacy-patcher",
|
||||
info_plist={
|
||||
"CFBundleName": "OpenCore Legacy Patcher",
|
||||
"CFBundleVersion": constants.Constants().patcher_version,
|
||||
"CFBundleShortVersionString": constants.Constants().patcher_version,
|
||||
"NSHumanReadableCopyright": constants.Constants().copyright_date,
|
||||
"LSMinimumSystemVersion": "10.10.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
OpenCore Legacy Patcher at its core is a Python-based GUI/CLI-based application. In turn, to run the project from source, you simply need to invoke the OpenCore-Patcher-GUI.command file via Python.
|
||||
|
||||
For developers wishing to validate mainline changes, you may use this link: [GUI (Graphical Based App)](https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/main/OpenCore-Patcher.app%20%28GUI%29.zip)
|
||||
For developers wishing to validate mainline changes, you may use this link: [GUI (Graphical Based App)](https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/main/OpenCore-Patcher.pkg.zip)
|
||||
|
||||
* **Warning**: Nightly builds (untagged builds built from the latest commit) are actively developed OpenCore Legacy Patcher builds. These builds have not been tested, are not guaranteed to work, and are not guaranteed to be safe. Do not use nightlies without a good reason to do so, and do not use them on your main machine. Additionally, these binaries should not be used without first consulting the [CHANGELOG](./CHANGELOG.md).
|
||||
|
||||
@@ -63,9 +63,7 @@ pip3 install pyinstaller
|
||||
# Move into project directory
|
||||
cd ~/Developer/OpenCore-Legacy-Patcher/
|
||||
# Create the pyinstaller based Application
|
||||
# Optional Arguments
|
||||
# '--reset_binaries': Redownload and generate support files
|
||||
python3 Build-Binary.command
|
||||
python3 Build-Project.command
|
||||
# Open build folder
|
||||
open ./dist/
|
||||
```
|
||||
|
||||
@@ -1,988 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PACKAGES</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>MUST-CLOSE-APPLICATION-ITEMS</key>
|
||||
<array/>
|
||||
<key>MUST-CLOSE-APPLICATIONS</key>
|
||||
<false/>
|
||||
<key>PACKAGE_FILES</key>
|
||||
<dict>
|
||||
<key>DEFAULT_INSTALL_LOCATION</key>
|
||||
<string>/</string>
|
||||
<key>HIERARCHY</key>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Applications</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>509</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BUNDLE_CAN_DOWNGRADE</key>
|
||||
<false/>
|
||||
<key>BUNDLE_POSTINSTALL_PATH</key>
|
||||
<dict>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>BUNDLE_PREINSTALL_PATH</key>
|
||||
<dict>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../dist/OpenCore-Patcher.app</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Dortania</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>2</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>509</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>2</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Application Support</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Automator</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Documentation</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Extensions</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Filesystems</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Frameworks</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Input Methods</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Internet Plug-Ins</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Keyboard Layouts</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../payloads/Launch Services/com.dortania.opencore-legacy-patcher.auto-patch.plist</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>420</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>LaunchAgents</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>LaunchDaemons</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>PreferencePanes</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Preferences</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Printers</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>PrivilegedHelperTools</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>1005</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>QuickLook</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>QuickTime</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Screen Savers</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Scripts</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Services</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Widgets</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Library</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Shared</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>1023</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Users</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>/</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>PAYLOAD_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PRESERVE_EXTENDED_ATTRIBUTES</key>
|
||||
<false/>
|
||||
<key>SHOW_INVISIBLE</key>
|
||||
<false/>
|
||||
<key>SPLIT_FORKS</key>
|
||||
<true/>
|
||||
<key>TREAT_MISSING_FILES_AS_WARNING</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
<key>PACKAGE_SCRIPTS</key>
|
||||
<dict>
|
||||
<key>POSTINSTALL_PATH</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>postinstall.sh</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>PREINSTALL_PATH</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>preinstall.sh</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>RESOURCES</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>PACKAGE_SETTINGS</key>
|
||||
<dict>
|
||||
<key>AUTHENTICATION</key>
|
||||
<integer>1</integer>
|
||||
<key>CONCLUSION_ACTION</key>
|
||||
<integer>0</integer>
|
||||
<key>FOLLOW_SYMBOLIC_LINKS</key>
|
||||
<false/>
|
||||
<key>IDENTIFIER</key>
|
||||
<string>com.dortania.pkg.AutoPkg-Assets</string>
|
||||
<key>LOCATION</key>
|
||||
<integer>0</integer>
|
||||
<key>NAME</key>
|
||||
<string>AutoPkg-Assets</string>
|
||||
<key>OVERWRITE_PERMISSIONS</key>
|
||||
<false/>
|
||||
<key>PAYLOAD_SIZE</key>
|
||||
<integer>-1</integer>
|
||||
<key>REFERENCE_PATH</key>
|
||||
<string></string>
|
||||
<key>RELOCATABLE</key>
|
||||
<false/>
|
||||
<key>USE_HFS+_COMPRESSION</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>UUID</key>
|
||||
<string>4312D78E-7981-41F2-A0E9-5C7E11AC61C5</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PROJECT</key>
|
||||
<dict>
|
||||
<key>PROJECT_COMMENTS</key>
|
||||
<dict>
|
||||
<key>NOTES</key>
|
||||
<data>
|
||||
</data>
|
||||
</dict>
|
||||
<key>PROJECT_PRESENTATION</key>
|
||||
<dict>
|
||||
<key>BACKGROUND</key>
|
||||
<dict>
|
||||
<key>APPAREANCES</key>
|
||||
<dict>
|
||||
<key>DARK_AQUA</key>
|
||||
<dict/>
|
||||
<key>LIGHT_AQUA</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<key>SHARED_SETTINGS_FOR_ALL_APPAREANCES</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>INSTALLATION TYPE</key>
|
||||
<dict>
|
||||
<key>HIERARCHIES</key>
|
||||
<dict>
|
||||
<key>INSTALLER</key>
|
||||
<dict>
|
||||
<key>LIST</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>DESCRIPTION</key>
|
||||
<array/>
|
||||
<key>OPTIONS</key>
|
||||
<dict>
|
||||
<key>HIDDEN</key>
|
||||
<false/>
|
||||
<key>STATE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>PACKAGE_UUID</key>
|
||||
<string>4312D78E-7981-41F2-A0E9-5C7E11AC61C5</string>
|
||||
<key>TITLE</key>
|
||||
<array/>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>UUID</key>
|
||||
<string>B3E23E4E-EF8D-4C21-933E-03C8187D415B</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>REMOVED</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>MODE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>INSTALLATION_STEPS</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key>
|
||||
<string>ICPresentationViewIntroductionController</string>
|
||||
<key>INSTALLER_PLUGIN</key>
|
||||
<string>Introduction</string>
|
||||
<key>LIST_TITLE_KEY</key>
|
||||
<string>InstallerSectionTitle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key>
|
||||
<string>ICPresentationViewReadMeController</string>
|
||||
<key>INSTALLER_PLUGIN</key>
|
||||
<string>ReadMe</string>
|
||||
<key>LIST_TITLE_KEY</key>
|
||||
<string>InstallerSectionTitle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key>
|
||||
<string>ICPresentationViewLicenseController</string>
|
||||
<key>INSTALLER_PLUGIN</key>
|
||||
<string>License</string>
|
||||
<key>LIST_TITLE_KEY</key>
|
||||
<string>InstallerSectionTitle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key>
|
||||
<string>ICPresentationViewDestinationSelectController</string>
|
||||
<key>INSTALLER_PLUGIN</key>
|
||||
<string>TargetSelect</string>
|
||||
<key>LIST_TITLE_KEY</key>
|
||||
<string>InstallerSectionTitle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key>
|
||||
<string>ICPresentationViewInstallationTypeController</string>
|
||||
<key>INSTALLER_PLUGIN</key>
|
||||
<string>PackageSelection</string>
|
||||
<key>LIST_TITLE_KEY</key>
|
||||
<string>InstallerSectionTitle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key>
|
||||
<string>ICPresentationViewInstallationController</string>
|
||||
<key>INSTALLER_PLUGIN</key>
|
||||
<string>Install</string>
|
||||
<key>LIST_TITLE_KEY</key>
|
||||
<string>InstallerSectionTitle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key>
|
||||
<string>ICPresentationViewSummaryController</string>
|
||||
<key>INSTALLER_PLUGIN</key>
|
||||
<string>Summary</string>
|
||||
<key>LIST_TITLE_KEY</key>
|
||||
<string>InstallerSectionTitle</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INTRODUCTION</key>
|
||||
<dict>
|
||||
<key>LOCALIZATIONS</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>LANGUAGE</key>
|
||||
<string>English</string>
|
||||
<key>VALUE</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>intro.txt</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>LICENSE</key>
|
||||
<dict>
|
||||
<key>LOCALIZATIONS</key>
|
||||
<array/>
|
||||
<key>MODE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>README</key>
|
||||
<dict>
|
||||
<key>LOCALIZATIONS</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>SUMMARY</key>
|
||||
<dict>
|
||||
<key>LOCALIZATIONS</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>TITLE</key>
|
||||
<dict>
|
||||
<key>LOCALIZATIONS</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>LANGUAGE</key>
|
||||
<string>English</string>
|
||||
<key>VALUE</key>
|
||||
<string>AutoPkg-Assets</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>PROJECT_REQUIREMENTS</key>
|
||||
<dict>
|
||||
<key>LIST</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BEHAVIOR</key>
|
||||
<integer>3</integer>
|
||||
<key>DICTIONARY</key>
|
||||
<dict>
|
||||
<key>IC_REQUIREMENT_FILES_CONDITION</key>
|
||||
<integer>1</integer>
|
||||
<key>IC_REQUIREMENT_FILES_DISK_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>IC_REQUIREMENT_FILES_LIST</key>
|
||||
<array>
|
||||
<string>/System/Library/CoreServices/OpenCore-Legacy-Patcher.plist</string>
|
||||
</array>
|
||||
<key>IC_REQUIREMENT_FILES_SELECTOR</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>IC_REQUIREMENT_CHECK_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>IDENTIFIER</key>
|
||||
<string>fr.whitebox.Packages.requirement.files</string>
|
||||
<key>MESSAGE</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>LANGUAGE</key>
|
||||
<string>English</string>
|
||||
<key>VALUE</key>
|
||||
<string>AutoPkg-Assets.pkg should never be used by end-users manually, please use the OpenCore-Patcher.app listed on Github</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NAME</key>
|
||||
<string>Files</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BEHAVIOR</key>
|
||||
<integer>3</integer>
|
||||
<key>DICTIONARY</key>
|
||||
<dict>
|
||||
<key>IC_REQUIREMENT_OS_DISK_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>IC_REQUIREMENT_OS_DISTRIBUTION_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>IC_REQUIREMENT_OS_MINIMUM_VERSION</key>
|
||||
<integer>110000</integer>
|
||||
</dict>
|
||||
<key>IC_REQUIREMENT_CHECK_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>IDENTIFIER</key>
|
||||
<string>fr.whitebox.Packages.requirement.os</string>
|
||||
<key>MESSAGE</key>
|
||||
<array/>
|
||||
<key>NAME</key>
|
||||
<string>Operating System</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>RESOURCES</key>
|
||||
<array/>
|
||||
<key>ROOT_VOLUME_ONLY</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>PROJECT_SETTINGS</key>
|
||||
<dict>
|
||||
<key>BUILD_FORMAT</key>
|
||||
<integer>0</integer>
|
||||
<key>BUILD_PATH</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>../../dist</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>EXCLUDED_FILES</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>PATTERNS_ARRAY</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>.DS_Store</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PROTECTED</key>
|
||||
<true/>
|
||||
<key>PROXY_NAME</key>
|
||||
<string>Remove .DS_Store files</string>
|
||||
<key>PROXY_TOOLTIP</key>
|
||||
<string>Remove ".DS_Store" files created by the Finder.</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>PATTERNS_ARRAY</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>.pbdevelopment</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PROTECTED</key>
|
||||
<true/>
|
||||
<key>PROXY_NAME</key>
|
||||
<string>Remove .pbdevelopment files</string>
|
||||
<key>PROXY_TOOLTIP</key>
|
||||
<string>Remove ".pbdevelopment" files created by ProjectBuilder or Xcode.</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>PATTERNS_ARRAY</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>CVS</string>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>.cvsignore</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>.cvspass</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>.svn</string>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>.git</string>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>.gitignore</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PROTECTED</key>
|
||||
<true/>
|
||||
<key>PROXY_NAME</key>
|
||||
<string>Remove SCM metadata</string>
|
||||
<key>PROXY_TOOLTIP</key>
|
||||
<string>Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems.</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>PATTERNS_ARRAY</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>classes.nib</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>designable.db</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>info.nib</string>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PROTECTED</key>
|
||||
<true/>
|
||||
<key>PROXY_NAME</key>
|
||||
<string>Optimize nib files</string>
|
||||
<key>PROXY_TOOLTIP</key>
|
||||
<string>Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles.</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>PATTERNS_ARRAY</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>REGULAR_EXPRESSION</key>
|
||||
<false/>
|
||||
<key>STRING</key>
|
||||
<string>Resources Disabled</string>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PROTECTED</key>
|
||||
<true/>
|
||||
<key>PROXY_NAME</key>
|
||||
<string>Remove Resources Disabled folders</string>
|
||||
<key>PROXY_TOOLTIP</key>
|
||||
<string>Remove "Resources Disabled" folders.</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>SEPARATOR</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NAME</key>
|
||||
<string>AutoPkg-Assets</string>
|
||||
<key>PAYLOAD_ONLY</key>
|
||||
<false/>
|
||||
<key>TREAT_MISSING_PRESENTATION_DOCUMENTS_AS_WARNING</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>VERSION</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
ci_tooling/autopkg/PkgBackground.png
Normal file
BIN
ci_tooling/autopkg/PkgBackground.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 KiB |
@@ -1,7 +0,0 @@
|
||||
DO NOT RUN AUTOPKG-ASSETS MANUALLY!
|
||||
|
||||
THIS CAN BREAK YOUR SYSTEM'S INSTALL!
|
||||
|
||||
This package should only ever be invoked by the Patcher itself, never downloaded or run by the user. Download the OpenCore-Patcher.app on the Github Repository.
|
||||
|
||||
https://github.com/dortania/OpenCore-Legacy-Patcher/releases/
|
||||
@@ -5,18 +5,41 @@
|
||||
# Create alias for app, start patching and reboot.
|
||||
# ------------------------------------------------------
|
||||
|
||||
# MARK: PackageKit Parameters
|
||||
# ---------------------------
|
||||
|
||||
pathToScript=$0 # ex. /tmp/PKInstallSandbox.*/Scripts/*/preinstall
|
||||
pathToPackage=$1 # ex. ~/Downloads/Installer.pkg
|
||||
pathToTargetLocation=$2 # ex. '/', '/Applications', etc (depends on pkgbuild's '--install-location' argument)
|
||||
pathToTargetVolume=$3 # ex. '/', '/Volumes/MyVolume', etc
|
||||
pathToStartupDisk=$4 # ex. '/'
|
||||
|
||||
|
||||
# MARK: Variables
|
||||
# ---------------------------
|
||||
|
||||
mainAppPath="/Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
shimAppPath="/Applications/OpenCore-Patcher.app"
|
||||
helperPath="Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper"
|
||||
mainAppPath="Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
shimAppPath="Applications/OpenCore-Patcher.app"
|
||||
executablePath="$mainAppPath/Contents/MacOS/OpenCore-Patcher"
|
||||
|
||||
|
||||
# MARK: Functions
|
||||
# ---------------------------
|
||||
|
||||
function _setSUIDBit() {
|
||||
local binaryPath=$1
|
||||
|
||||
echo "Setting SUID bit on: $binaryPath"
|
||||
|
||||
# Check if path is a directory
|
||||
if [[ -d $binaryPath ]]; then
|
||||
/bin/chmod -R +s $binaryPath
|
||||
else
|
||||
/bin/chmod +s $binaryPath
|
||||
fi
|
||||
}
|
||||
|
||||
function _createAlias() {
|
||||
local mainPath=$1
|
||||
local aliasPath=$2
|
||||
@@ -25,13 +48,16 @@ function _createAlias() {
|
||||
if [[ -e $aliasPath ]]; then
|
||||
# Check if alias path is a symbolic link
|
||||
if [[ -L $aliasPath ]]; then
|
||||
echo "Removing old symbolic link: $aliasPath"
|
||||
/bin/rm -f $aliasPath
|
||||
else
|
||||
echo "Removing old file: $aliasPath"
|
||||
/bin/rm -rf $aliasPath
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create symbolic link
|
||||
echo "Creating symbolic link: $aliasPath"
|
||||
/bin/ln -s $mainPath $aliasPath
|
||||
}
|
||||
|
||||
@@ -47,13 +73,25 @@ function _logFile() {
|
||||
echo "/Users/Shared/.OCLP-AutoPatcher-Log-$(/bin/date +"%Y_%m_%d_%I_%M_%p").txt"
|
||||
}
|
||||
|
||||
function _fixSettingsFilePermission() {
|
||||
local settingsPath="$pathToTargetVolume/Users/Shared/.com.dortania.opencore-legacy-patcher.plist"
|
||||
|
||||
if [[ -e $settingsPath ]]; then
|
||||
echo "Fixing settings file permissions: $settingsPath"
|
||||
/bin/chmod 666 $settingsPath
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function _reboot() {
|
||||
/sbin/reboot
|
||||
}
|
||||
|
||||
function _main() {
|
||||
_createAlias "$mainAppPath" "$shimAppPath"
|
||||
_startPatching "$executablePath"
|
||||
_setSUIDBit "$pathToTargetVolume/$helperPath"
|
||||
_createAlias "$pathToTargetVolume/$mainAppPath" "$pathToTargetVolume/$shimAppPath"
|
||||
_startPatching "$pathToTargetVolume/$executablePath"
|
||||
_fixSettingsFilePermission
|
||||
_reboot
|
||||
}
|
||||
|
||||
@@ -61,4 +99,5 @@ function _main() {
|
||||
# MARK: Main
|
||||
# ---------------------------
|
||||
|
||||
echo "Starting postinstall script..."
|
||||
_main
|
||||
@@ -6,14 +6,25 @@
|
||||
# ------------------------------------------------------
|
||||
|
||||
|
||||
# MARK: PackageKit Parameters
|
||||
# ---------------------------
|
||||
|
||||
pathToScript=$0 # ex. /tmp/PKInstallSandbox.*/Scripts/*/preinstall
|
||||
pathToPackage=$1 # ex. ~/Downloads/Installer.pkg
|
||||
pathToTargetLocation=$2 # ex. '/', '/Applications', etc (depends on pkgbuild's '--install-location' argument)
|
||||
pathToTargetVolume=$3 # ex. '/', '/Volumes/MyVolume', etc
|
||||
pathToStartupDisk=$4 # ex. '/'
|
||||
|
||||
|
||||
# MARK: Variables
|
||||
# ---------------------------
|
||||
|
||||
filesToRemove=(
|
||||
"/Applications/OpenCore-Patcher.app"
|
||||
"/Library/Application Support/Dortania/Update.plist"
|
||||
"/Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
"/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"
|
||||
"Applications/OpenCore-Patcher.app"
|
||||
"Library/Application Support/Dortania/Update.plist"
|
||||
"Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
"Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"
|
||||
"Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper"
|
||||
)
|
||||
|
||||
|
||||
@@ -21,39 +32,43 @@ filesToRemove=(
|
||||
# ---------------------------
|
||||
|
||||
function _removeFile() {
|
||||
local currentFile=$1
|
||||
local file=$1
|
||||
|
||||
if [[ ! -e $currentFile ]]; then
|
||||
if [[ ! -e $file ]]; then
|
||||
# Check if file is a symbolic link
|
||||
if [[ -L $currentFile ]]; then
|
||||
/bin/rm -f $currentFile
|
||||
if [[ -L $file ]]; then
|
||||
echo "Removing symbolic link: $file"
|
||||
/bin/rm -f $file
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Removing file: $file"
|
||||
|
||||
# Check if file is a directory
|
||||
if [[ -d $currentFile ]]; then
|
||||
/bin/rm -rf $currentFile
|
||||
if [[ -d $file ]]; then
|
||||
/bin/rm -rf $file
|
||||
else
|
||||
/bin/rm -f $currentFile
|
||||
/bin/rm -f $file
|
||||
fi
|
||||
}
|
||||
|
||||
function _createParentDirectory() {
|
||||
local currentFile=$1
|
||||
local file=$1
|
||||
|
||||
local parentDirectory=$(/usr/bin/dirname $currentFile)
|
||||
local parentDirectory="$(/usr/bin/dirname $file)"
|
||||
|
||||
# Check if parent directory exists
|
||||
if [[ ! -d $parentDirectory ]]; then
|
||||
echo "Creating parent directory: $parentDirectory"
|
||||
/bin/mkdir -p $parentDirectory
|
||||
fi
|
||||
}
|
||||
|
||||
function _main() {
|
||||
for file in $filesToRemove; do
|
||||
_removeFile $file
|
||||
_createParentDirectory $file
|
||||
_removeFile $pathToTargetVolume/$file
|
||||
_createParentDirectory $pathToTargetVolume/$file
|
||||
done
|
||||
}
|
||||
|
||||
@@ -61,4 +76,5 @@ function _main() {
|
||||
# MARK: Main
|
||||
# ---------------------------
|
||||
|
||||
echo "Starting preinstall script..."
|
||||
_main
|
||||
176
ci_tooling/build_modules/application.py
Normal file
176
ci_tooling/build_modules/application.py
Normal file
@@ -0,0 +1,176 @@
|
||||
import sys
|
||||
import time
|
||||
import plistlib
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from opencore_legacy_patcher import constants
|
||||
from opencore_legacy_patcher.support import subprocess_wrapper
|
||||
|
||||
|
||||
class GenerateApplication:
|
||||
"""
|
||||
Generate OpenCore-Patcher.app
|
||||
"""
|
||||
|
||||
def __init__(self, reset_pyinstaller_cache: bool = False, git_branch: str = None, git_commit_url: str = None, git_commit_date: str = None, analytics_key: str = None, analytics_endpoint: str = None) -> None:
|
||||
"""
|
||||
Initialize
|
||||
"""
|
||||
self._pyinstaller = [sys.executable, "-m", "PyInstaller"]
|
||||
self._application_output = Path("./dist/OpenCore-Patcher.app")
|
||||
|
||||
self._reset_pyinstaller_cache = reset_pyinstaller_cache
|
||||
|
||||
self._git_branch = git_branch
|
||||
self._git_commit_url = git_commit_url
|
||||
self._git_commit_date = git_commit_date
|
||||
|
||||
self._analytics_key = analytics_key
|
||||
self._analytics_endpoint = analytics_endpoint
|
||||
|
||||
|
||||
def _generate_application(self) -> None:
|
||||
"""
|
||||
Generate PyInstaller Application
|
||||
"""
|
||||
if self._application_output.exists():
|
||||
subprocess_wrapper.run_and_verify(["/bin/rm", "-rf", self._application_output], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
print("Generating OpenCore-Patcher.app")
|
||||
_args = self._pyinstaller + ["./OpenCore-Patcher-GUI.spec", "--noconfirm"]
|
||||
if self._reset_pyinstaller_cache:
|
||||
_args.append("--clean")
|
||||
|
||||
subprocess_wrapper.run_and_verify(_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
|
||||
def _embed_analytics_key(self) -> None:
|
||||
"""
|
||||
Embed analytics key
|
||||
"""
|
||||
_file = Path("./opencore_legacy_patcher/support/analytics_handler.py")
|
||||
|
||||
if not all([self._analytics_key, self._analytics_endpoint]):
|
||||
print("Analytics key or endpoint not provided, skipping embedding")
|
||||
return
|
||||
|
||||
print("Embedding analytics data")
|
||||
if not Path(_file).exists():
|
||||
raise FileNotFoundError("analytics_handler.py not found")
|
||||
|
||||
lines = []
|
||||
with open(_file, "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("SITE_KEY: str = "):
|
||||
lines[i] = f"SITE_KEY: str = \"{self._analytics_key}\"\n"
|
||||
elif line.startswith("ANALYTICS_SERVER: str = "):
|
||||
lines[i] = f"ANALYTICS_SERVER: str = \"{self._analytics_endpoint}\"\n"
|
||||
|
||||
with open(_file, "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
||||
def _remove_analytics_key(self) -> None:
|
||||
"""
|
||||
Remove analytics key
|
||||
"""
|
||||
_file = Path("./opencore_legacy_patcher/support/analytics_handler.py")
|
||||
|
||||
if not all([self._analytics_key, self._analytics_endpoint]):
|
||||
return
|
||||
|
||||
print("Removing analytics data")
|
||||
if not _file.exists():
|
||||
raise FileNotFoundError("analytics_handler.py not found")
|
||||
|
||||
lines = []
|
||||
with open(_file, "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("SITE_KEY: str = "):
|
||||
lines[i] = "SITE_KEY: str = \"\"\n"
|
||||
elif line.startswith("ANALYTICS_SERVER: str = "):
|
||||
lines[i] = "ANALYTICS_SERVER: str = \"\"\n"
|
||||
|
||||
with open(_file, "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
||||
def _patch_load_command(self):
|
||||
"""
|
||||
Patch LC_VERSION_MIN_MACOSX in Load Command to report 10.10
|
||||
|
||||
By default Pyinstaller will create binaries supporting 10.13+
|
||||
However this limitation is entirely arbitrary for our libraries
|
||||
and instead we're able to support 10.10 without issues.
|
||||
|
||||
To verify set version:
|
||||
otool -l ./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher
|
||||
|
||||
cmd LC_VERSION_MIN_MACOSX
|
||||
cmdsize 16
|
||||
version 10.13
|
||||
sdk 10.9
|
||||
"""
|
||||
_file = self._application_output / "Contents" / "MacOS" / "OpenCore-Patcher"
|
||||
|
||||
_find = b'\x00\x0D\x0A\x00' # 10.13 (0xA0D)
|
||||
_replace = b'\x00\x0A\x0A\x00' # 10.10 (0xA0A)
|
||||
|
||||
print("Patching LC_VERSION_MIN_MACOSX")
|
||||
with open(_file, "rb") as f:
|
||||
data = f.read()
|
||||
data = data.replace(_find, _replace, 1)
|
||||
|
||||
with open(_file, "wb") as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
def _embed_git_data(self) -> None:
|
||||
"""
|
||||
Embed git data
|
||||
"""
|
||||
_file = self._application_output / "Contents" / "Info.plist"
|
||||
|
||||
_git_branch = self._git_branch or "Built from source"
|
||||
_git_commit = self._git_commit_url or ""
|
||||
_git_commit_date = self._git_commit_date or time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
|
||||
print("Embedding git data")
|
||||
_plist = plistlib.load(_file.open("rb"))
|
||||
_plist["Github"] = {
|
||||
"Branch": _git_branch,
|
||||
"Commit URL": _git_commit,
|
||||
"Commit Date": _git_commit_date
|
||||
}
|
||||
plistlib.dump(_plist, _file.open("wb"), sort_keys=True)
|
||||
|
||||
|
||||
def _embed_resources(self) -> None:
|
||||
"""
|
||||
Embed resources
|
||||
"""
|
||||
print("Embedding resources")
|
||||
for file in Path("payloads/Icon/AppIcons").glob("*.icns"):
|
||||
subprocess_wrapper.run_and_verify(
|
||||
["/bin/cp", str(file), self._application_output / "Contents" / "Resources/"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
|
||||
def generate(self) -> None:
|
||||
"""
|
||||
Generate OpenCore-Patcher.app
|
||||
"""
|
||||
self._embed_analytics_key()
|
||||
self._generate_application()
|
||||
self._remove_analytics_key()
|
||||
|
||||
self._patch_load_command()
|
||||
self._embed_git_data()
|
||||
self._embed_resources()
|
||||
136
ci_tooling/build_modules/disk_images.py
Normal file
136
ci_tooling/build_modules/disk_images.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
disk_images.py: Fetch and generate disk images (Universal-Binaries.dmg, payloads.dmg)
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from opencore_legacy_patcher import constants
|
||||
from opencore_legacy_patcher.support import subprocess_wrapper
|
||||
|
||||
|
||||
|
||||
class GenerateDiskImages:
|
||||
|
||||
def __init__(self, reset_dmg_cache: bool = False) -> None:
|
||||
"""
|
||||
Initialize
|
||||
"""
|
||||
self.reset_dmg_cache = reset_dmg_cache
|
||||
|
||||
|
||||
def _delete_extra_binaries(self):
|
||||
"""
|
||||
Delete extra binaries from payloads directory
|
||||
"""
|
||||
|
||||
whitelist_folders = [
|
||||
"ACPI",
|
||||
"Config",
|
||||
"Drivers",
|
||||
"Icon",
|
||||
"Kexts",
|
||||
"OpenCore",
|
||||
"Tools",
|
||||
"Launch Services",
|
||||
]
|
||||
|
||||
whitelist_files = []
|
||||
|
||||
print("Deleting extra binaries...")
|
||||
for file in Path("payloads").glob(pattern="*"):
|
||||
if file.is_dir():
|
||||
if file.name in whitelist_folders:
|
||||
continue
|
||||
print(f"- Deleting {file.name}")
|
||||
subprocess_wrapper.run_and_verify(["/bin/rm", "-rf", file])
|
||||
else:
|
||||
if file.name in whitelist_files:
|
||||
continue
|
||||
print(f"- Deleting {file.name}")
|
||||
subprocess_wrapper.run_and_verify(["/bin/rm", "-f", file])
|
||||
|
||||
|
||||
|
||||
def _generate_payloads_dmg(self):
|
||||
"""
|
||||
Generate disk image containing all payloads
|
||||
Disk image will be password protected due to issues with
|
||||
Apple's notarization system and inclusion of kernel extensions
|
||||
"""
|
||||
|
||||
if Path("./payloads.dmg").exists():
|
||||
if self.reset_dmg_cache is False:
|
||||
print("- payloads.dmg already exists, skipping creation")
|
||||
return
|
||||
|
||||
print("- Removing old payloads.dmg")
|
||||
subprocess_wrapper.run_and_verify(
|
||||
["/bin/rm", "-rf", "./payloads.dmg"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
print("Generating DMG...")
|
||||
subprocess_wrapper.run_and_verify([
|
||||
'/usr/bin/hdiutil', 'create', './payloads.dmg',
|
||||
'-megabytes', '32000', # Overlays can only be as large as the disk image allows
|
||||
'-format', 'UDZO', '-ov',
|
||||
'-volname', 'OpenCore Patcher Resources (Base)',
|
||||
'-fs', 'HFS+',
|
||||
'-srcfolder', './payloads',
|
||||
'-passphrase', 'password', '-encryption'
|
||||
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
print("DMG generation complete")
|
||||
|
||||
|
||||
def _download_resources(self):
|
||||
"""
|
||||
Download required dependencies
|
||||
"""
|
||||
|
||||
patcher_support_pkg_version = constants.Constants().patcher_support_pkg_version
|
||||
required_resources = [
|
||||
"Universal-Binaries.dmg"
|
||||
]
|
||||
|
||||
print("Downloading required resources...")
|
||||
for resource in required_resources:
|
||||
if Path(f"./{resource}").exists():
|
||||
if self.reset_dmg_cache is True:
|
||||
print(f" - Removing old {resource}")
|
||||
# Just to be safe
|
||||
assert resource, "Resource cannot be empty"
|
||||
assert resource not in ("/", "."), "Resource cannot be root"
|
||||
subprocess_wrapper.run_and_verify(
|
||||
["/bin/rm", "-rf", f"./{resource}"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
else:
|
||||
print(f"- {resource} already exists, skipping download")
|
||||
continue
|
||||
|
||||
print(f"- Downloading {resource}...")
|
||||
|
||||
subprocess_wrapper.run_and_verify(
|
||||
[
|
||||
"/usr/bin/curl", "-LO",
|
||||
f"https://github.com/dortania/PatcherSupportPkg/releases/download/{patcher_support_pkg_version}/{resource}"
|
||||
],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
if not Path(f"./{resource}").exists():
|
||||
print(f"- {resource} not found")
|
||||
raise Exception(f"{resource} not found")
|
||||
|
||||
|
||||
def generate(self) -> None:
|
||||
"""
|
||||
Generate disk images
|
||||
"""
|
||||
|
||||
self._delete_extra_binaries()
|
||||
self._generate_payloads_dmg()
|
||||
self._download_resources()
|
||||
110
ci_tooling/build_modules/package.py
Normal file
110
ci_tooling/build_modules/package.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
package.py: Generate packages (Installer, Uninstaller, AutoPkg-Assets)
|
||||
"""
|
||||
|
||||
import macos_pkg_builder
|
||||
from opencore_legacy_patcher import constants
|
||||
|
||||
|
||||
class GeneratePackage:
|
||||
"""
|
||||
Generate OpenCore-Patcher.pkg
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Initialize
|
||||
"""
|
||||
self._files = {
|
||||
"./dist/OpenCore-Patcher.app": "/Library/Application Support/Dortania/OpenCore-Patcher.app",
|
||||
"./ci_tooling/privileged_helper_tool/com.dortania.opencore-legacy-patcher.privileged-helper": "/Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper",
|
||||
}
|
||||
self._autopkg_files = {
|
||||
"./payloads/Launch Services/com.dortania.opencore-legacy-patcher.auto-patch.plist": "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist",
|
||||
}
|
||||
self._autopkg_files.update(self._files)
|
||||
|
||||
|
||||
def _generate_installer_welcome(self) -> str:
|
||||
"""
|
||||
Generate Welcome message for installer PKG
|
||||
"""
|
||||
_welcome = ""
|
||||
|
||||
_welcome += "# Overview\n"
|
||||
_welcome += f"This package will install the OpenCore Legacy Patcher application (v{constants.Constants().patcher_version}) on your system."
|
||||
|
||||
_welcome += "\n\nAdditionally, a shortcut for OpenCore Legacy Patcher will be added in the '/Applications' folder."
|
||||
_welcome += "\n\nThis package will not 'Build and Install OpenCore' or install any 'Root Patches' on your machine. If required, you can run OpenCore Legacy Patcher to install any patches you may need."
|
||||
_welcome += f"\n\nFor more information on OpenCore Legacy Patcher usage, see our [documentation]({constants.Constants().guide_link}) and [GitHub repository]({constants.Constants().repo_link})."
|
||||
_welcome += "\n\n"
|
||||
|
||||
_welcome += "## Files Installed"
|
||||
_welcome += "\n\nInstallation of this package will add the following files to your system:"
|
||||
for key, value in self._files.items():
|
||||
_welcome += f"\n\n- `{value}`"
|
||||
|
||||
return _welcome
|
||||
|
||||
|
||||
def _generate_uninstaller_welcome(self) -> str:
|
||||
"""
|
||||
Generate Welcome message for uninstaller PKG
|
||||
"""
|
||||
_welcome = ""
|
||||
|
||||
_welcome += "# Application Uninstaller\n"
|
||||
_welcome += "This package will uninstall the OpenCore Legacy Patcher application and its Privileged Helper Tool from your system."
|
||||
_welcome += "\n\n"
|
||||
_welcome += "This will not remove any root patches or OpenCore configurations that you may have installed using OpenCore Legacy Patcher."
|
||||
_welcome += "\n\n"
|
||||
_welcome += f"For more information on OpenCore Legacy Patcher, see our [documentation]({constants.Constants().guide_link}) and [GitHub repository]({constants.Constants().repo_link})."
|
||||
|
||||
return _welcome
|
||||
|
||||
|
||||
def generate(self) -> None:
|
||||
"""
|
||||
Generate OpenCore-Patcher.pkg
|
||||
"""
|
||||
print("Generating OpenCore-Patcher-Uninstaller.pkg")
|
||||
assert macos_pkg_builder.Packages(
|
||||
pkg_output="./dist/OpenCore-Patcher-Uninstaller.pkg",
|
||||
pkg_bundle_id="com.dortania.opencore-legacy-patcher-uninstaller",
|
||||
pkg_version=constants.Constants().patcher_version,
|
||||
pkg_background="./ci_tooling/installation_pkg/PkgBackgroundUninstaller.png",
|
||||
pkg_preinstall_script="./ci_tooling/installation_pkg/uninstall.sh",
|
||||
pkg_as_distribution=True,
|
||||
pkg_title="OpenCore Legacy Patcher Uninstaller",
|
||||
pkg_welcome=self._generate_uninstaller_welcome(),
|
||||
).build() is True
|
||||
|
||||
print("Generating OpenCore-Patcher.pkg")
|
||||
assert macos_pkg_builder.Packages(
|
||||
pkg_output="./dist/OpenCore-Patcher.pkg",
|
||||
pkg_bundle_id="com.dortania.opencore-legacy-patcher",
|
||||
pkg_version=constants.Constants().patcher_version,
|
||||
pkg_allow_relocation=False,
|
||||
pkg_as_distribution=True,
|
||||
pkg_background="./ci_tooling/installation_pkg/PkgBackground.png",
|
||||
pkg_preinstall_script="./ci_tooling/installation_pkg/preinstall.sh",
|
||||
pkg_postinstall_script="./ci_tooling/installation_pkg/postinstall.sh",
|
||||
pkg_file_structure=self._files,
|
||||
pkg_title="OpenCore Legacy Patcher",
|
||||
pkg_welcome=self._generate_installer_welcome(),
|
||||
).build() is True
|
||||
|
||||
print("Generating AutoPkg-Assets.pkg")
|
||||
assert macos_pkg_builder.Packages(
|
||||
pkg_output="./dist/AutoPkg-Assets.pkg",
|
||||
pkg_bundle_id="com.dortania.pkg.AutoPkg-Assets",
|
||||
pkg_version=constants.Constants().patcher_version,
|
||||
pkg_allow_relocation=False,
|
||||
pkg_as_distribution=True,
|
||||
pkg_background="./ci_tooling/autopkg/PkgBackground.png",
|
||||
pkg_preinstall_script="./ci_tooling/autopkg/preinstall.sh",
|
||||
pkg_postinstall_script="./ci_tooling/autopkg/postinstall.sh",
|
||||
pkg_file_structure=self._autopkg_files,
|
||||
pkg_title="AutoPkg Assets",
|
||||
pkg_welcome="# DO NOT RUN AUTOPKG-ASSETS MANUALLY!\n\n## THIS CAN BREAK YOUR SYSTEM'S INSTALL!\n\nThis package should only ever be invoked by the Patcher itself, never downloaded or run by the user. Download the OpenCore-Patcher.pkg on the Github Repository.\n\n[OpenCore Legacy Patcher GitHub Release](https://github.com/dortania/OpenCore-Legacy-Patcher/releases/)",
|
||||
).build() is True
|
||||
33
ci_tooling/build_modules/shim.py
Normal file
33
ci_tooling/build_modules/shim.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
shim.py: Generate Update Shim
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from opencore_legacy_patcher.support import subprocess_wrapper
|
||||
|
||||
|
||||
class GenerateShim:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._shim_path = "./ci_tooling/update_shim/OpenCore-Patcher.app"
|
||||
self._shim_pkg = f"{self._shim_path}/Contents/Resources/OpenCore-Patcher.pkg"
|
||||
|
||||
self._build_pkg = "./dist/OpenCore-Patcher.pkg"
|
||||
self._output_shim = "./dist/OpenCore-Patcher (Shim).app"
|
||||
|
||||
|
||||
def generate(self) -> None:
|
||||
"""
|
||||
Generate Update Shim
|
||||
"""
|
||||
print("Generating Update Shim")
|
||||
if Path(self._shim_pkg).exists():
|
||||
Path(self._shim_pkg).unlink()
|
||||
|
||||
subprocess_wrapper.run_and_verify(["/bin/cp", "-R", self._build_pkg, self._shim_pkg])
|
||||
|
||||
if Path(self._output_shim).exists():
|
||||
Path(self._output_shim).unlink()
|
||||
|
||||
subprocess_wrapper.run_and_verify(["/bin/cp", "-R", self._shim_path, self._output_shim])
|
||||
54
ci_tooling/build_modules/sign_notarize.py
Normal file
54
ci_tooling/build_modules/sign_notarize.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
sign_notarize.py: Sign and Notarize a file
|
||||
"""
|
||||
|
||||
import mac_signing_buddy
|
||||
import macos_pkg_builder
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import macos_pkg_builder.utilities.signing
|
||||
|
||||
|
||||
class SignAndNotarize:
|
||||
|
||||
def __init__(self, path: Path, signing_identity: str, notarization_apple_id: str, notarization_password: str, notarization_team_id: str, entitlements: str = None) -> None:
|
||||
"""
|
||||
Initialize
|
||||
"""
|
||||
self._path = path
|
||||
self._signing_identity = signing_identity
|
||||
self._notarization_apple_id = notarization_apple_id
|
||||
self._notarization_password = notarization_password
|
||||
self._notarization_team_id = notarization_team_id
|
||||
self._entitlements = entitlements
|
||||
|
||||
|
||||
def sign_and_notarize(self) -> None:
|
||||
"""
|
||||
Sign and Notarize
|
||||
"""
|
||||
if not all([self._signing_identity, self._notarization_apple_id, self._notarization_password, self._notarization_team_id]):
|
||||
print("Signing and Notarization details not provided, skipping")
|
||||
return
|
||||
|
||||
print(f"Signing {self._path.name}")
|
||||
if self._path.name.endswith(".pkg"):
|
||||
macos_pkg_builder.utilities.signing.SignPackage(
|
||||
identity=self._signing_identity,
|
||||
pkg=self._path,
|
||||
).sign()
|
||||
else:
|
||||
mac_signing_buddy.Sign(
|
||||
identity=self._signing_identity,
|
||||
file=self._path,
|
||||
**({"entitlements": self._entitlements} if self._entitlements else {}),
|
||||
).sign()
|
||||
|
||||
print(f"Notarizing {self._path.name}")
|
||||
mac_signing_buddy.Notarize(
|
||||
apple_id=self._notarization_apple_id,
|
||||
password=self._notarization_password,
|
||||
team_id=self._notarization_team_id,
|
||||
file=self._path,
|
||||
).sign()
|
||||
BIN
ci_tooling/installation_pkg/PkgBackground-Source-File.afdesign
Normal file
BIN
ci_tooling/installation_pkg/PkgBackground-Source-File.afdesign
Normal file
Binary file not shown.
BIN
ci_tooling/installation_pkg/PkgBackground.png
Normal file
BIN
ci_tooling/installation_pkg/PkgBackground.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 320 KiB |
BIN
ci_tooling/installation_pkg/PkgBackgroundUninstaller.png
Normal file
BIN
ci_tooling/installation_pkg/PkgBackgroundUninstaller.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
74
ci_tooling/installation_pkg/postinstall.sh
Normal file
74
ci_tooling/installation_pkg/postinstall.sh
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/bin/zsh --no-rcs
|
||||
# ------------------------------------------------------
|
||||
# OpenCore Legacy Patcher PKG Post Install Script
|
||||
# ------------------------------------------------------
|
||||
# Set SUID bit on helper tool, and create app alias.
|
||||
# ------------------------------------------------------
|
||||
|
||||
|
||||
# MARK: PackageKit Parameters
|
||||
# ---------------------------
|
||||
|
||||
pathToScript=$0 # ex. /tmp/PKInstallSandbox.*/Scripts/*/preinstall
|
||||
pathToPackage=$1 # ex. ~/Downloads/Installer.pkg
|
||||
pathToTargetLocation=$2 # ex. '/', '/Applications', etc (depends on pkgbuild's '--install-location' argument)
|
||||
pathToTargetVolume=$3 # ex. '/', '/Volumes/MyVolume', etc
|
||||
pathToStartupDisk=$4 # ex. '/'
|
||||
|
||||
|
||||
# MARK: Variables
|
||||
# ---------------------------
|
||||
|
||||
helperPath="Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper"
|
||||
mainAppPath="Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
shimAppPath="Applications/OpenCore-Patcher.app"
|
||||
|
||||
|
||||
# MARK: Functions
|
||||
# ---------------------------
|
||||
|
||||
function _setSUIDBit() {
|
||||
local binaryPath=$1
|
||||
|
||||
echo "Setting SUID bit on: $binaryPath"
|
||||
|
||||
# Check if path is a directory
|
||||
if [[ -d $binaryPath ]]; then
|
||||
/bin/chmod -R +s $binaryPath
|
||||
else
|
||||
/bin/chmod +s $binaryPath
|
||||
fi
|
||||
}
|
||||
|
||||
function _createAlias() {
|
||||
local mainPath=$1
|
||||
local aliasPath=$2
|
||||
|
||||
# Check if alias path exists
|
||||
if [[ -e $aliasPath ]]; then
|
||||
# Check if alias path is a symbolic link
|
||||
if [[ -L $aliasPath ]]; then
|
||||
echo "Removing old symbolic link: $aliasPath"
|
||||
/bin/rm -f $aliasPath
|
||||
else
|
||||
echo "Removing old file: $aliasPath"
|
||||
/bin/rm -rf $aliasPath
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create symbolic link
|
||||
echo "Creating symbolic link: $aliasPath"
|
||||
/bin/ln -s $mainPath $aliasPath
|
||||
}
|
||||
|
||||
function _main() {
|
||||
_setSUIDBit "$pathToTargetVolume/$helperPath"
|
||||
_createAlias "$pathToTargetVolume/$mainAppPath" "$pathToTargetVolume/$shimAppPath"
|
||||
}
|
||||
|
||||
|
||||
# MARK: Main
|
||||
# ---------------------------
|
||||
|
||||
echo "Starting postinstall script..."
|
||||
_main
|
||||
79
ci_tooling/installation_pkg/preinstall.sh
Normal file
79
ci_tooling/installation_pkg/preinstall.sh
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/bin/zsh --no-rcs
|
||||
# ------------------------------------------------------
|
||||
# OpenCore Legacy Patcher PKG Preinstall Script
|
||||
# ------------------------------------------------------
|
||||
# Remove old files, and prepare directories.
|
||||
# ------------------------------------------------------
|
||||
|
||||
|
||||
# MARK: PackageKit Parameters
|
||||
# ---------------------------
|
||||
|
||||
pathToScript=$0 # ex. /tmp/PKInstallSandbox.*/Scripts/*/preinstall
|
||||
pathToPackage=$1 # ex. ~/Downloads/Installer.pkg
|
||||
pathToTargetLocation=$2 # ex. '/', '/Applications', etc (depends on pkgbuild's '--install-location' argument)
|
||||
pathToTargetVolume=$3 # ex. '/', '/Volumes/MyVolume', etc
|
||||
pathToStartupDisk=$4 # ex. '/'
|
||||
|
||||
|
||||
# MARK: Variables
|
||||
# ---------------------------
|
||||
|
||||
filesToRemove=(
|
||||
"Applications/OpenCore-Patcher.app"
|
||||
"Library/Application Support/Dortania/Update.plist"
|
||||
"Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
"Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper"
|
||||
)
|
||||
|
||||
|
||||
# MARK: Functions
|
||||
# ---------------------------
|
||||
|
||||
function _removeFile() {
|
||||
local file=$1
|
||||
|
||||
if [[ ! -e $file ]]; then
|
||||
# Check if file is a symbolic link
|
||||
if [[ -L $file ]]; then
|
||||
echo "Removing symbolic link: $file"
|
||||
/bin/rm -f $file
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Removing file: $file"
|
||||
|
||||
# Check if file is a directory
|
||||
if [[ -d $file ]]; then
|
||||
/bin/rm -rf $file
|
||||
else
|
||||
/bin/rm -f $file
|
||||
fi
|
||||
}
|
||||
|
||||
function _createParentDirectory() {
|
||||
local file=$1
|
||||
|
||||
local parentDirectory="$(/usr/bin/dirname $file)"
|
||||
|
||||
# Check if parent directory exists
|
||||
if [[ ! -d $parentDirectory ]]; then
|
||||
echo "Creating parent directory: $parentDirectory"
|
||||
/bin/mkdir -p $parentDirectory
|
||||
fi
|
||||
}
|
||||
|
||||
function _main() {
|
||||
for file in $filesToRemove; do
|
||||
_removeFile $pathToTargetVolume/$file
|
||||
_createParentDirectory $pathToTargetVolume/$file
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# MARK: Main
|
||||
# ---------------------------
|
||||
|
||||
echo "Starting preinstall script..."
|
||||
_main
|
||||
85
ci_tooling/installation_pkg/uninstall.sh
Normal file
85
ci_tooling/installation_pkg/uninstall.sh
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/bin/zsh --no-rcs
|
||||
# ------------------------------------------------------
|
||||
# OpenCore Legacy Patcher PKG Uninstall Script
|
||||
# ------------------------------------------------------
|
||||
|
||||
|
||||
# MARK: PackageKit Parameters
|
||||
# ---------------------------
|
||||
|
||||
pathToScript=$0 # ex. /tmp/PKInstallSandbox.*/Scripts/*/preinstall
|
||||
pathToPackage=$1 # ex. ~/Downloads/Installer.pkg
|
||||
pathToTargetLocation=$2 # ex. '/', '/Applications', etc (depends on pkgbuild's '--install-location' argument)
|
||||
pathToTargetVolume=$3 # ex. '/', '/Volumes/MyVolume', etc
|
||||
pathToStartupDisk=$4 # ex. '/'
|
||||
|
||||
|
||||
# MARK: Variables
|
||||
# ---------------------------
|
||||
|
||||
filesToRemove=(
|
||||
"Applications/OpenCore-Patcher.app"
|
||||
"Library/Application Support/Dortania/Update.plist"
|
||||
"Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
"Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper"
|
||||
)
|
||||
|
||||
|
||||
# MARK: Functions
|
||||
# ---------------------------
|
||||
|
||||
function _removeFile() {
|
||||
local file=$1
|
||||
|
||||
if [[ ! -e $file ]]; then
|
||||
# Check if file is a symbolic link
|
||||
if [[ -L $file ]]; then
|
||||
echo "Removing symbolic link: $file"
|
||||
/bin/rm -f $file
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Removing file: $file"
|
||||
|
||||
# Check if file is a directory
|
||||
if [[ -d $file ]]; then
|
||||
/bin/rm -rf $file
|
||||
else
|
||||
/bin/rm -f $file
|
||||
fi
|
||||
}
|
||||
|
||||
function _cleanLaunchService() {
|
||||
local domain="com.dortania.opencore-legacy-patcher"
|
||||
|
||||
# Iterate over launch agents and daemons
|
||||
for launchServiceVariant in "$pathToTargetVolume/Library/LaunchAgents" "$pathToTargetVolume/Library/LaunchDaemons"; do
|
||||
# Check if directory exists
|
||||
if [[ ! -d $launchServiceVariant ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Iterate over launch service files
|
||||
for launchServiceFile in $(/bin/ls -1 $launchServiceVariant | /usr/bin/grep $domain); do
|
||||
local launchServicePath="$launchServiceVariant/$launchServiceFile"
|
||||
|
||||
# Remove launch service file
|
||||
_removeFile $launchServicePath
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
function _main() {
|
||||
_cleanLaunchService
|
||||
for file in $filesToRemove; do
|
||||
_removeFile "$pathToTargetVolume/$file"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# MARK: Main
|
||||
# ---------------------------
|
||||
|
||||
echo "Starting uninstall script..."
|
||||
_main
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>OpenCore-Patcher</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>OpenCore-Patcher</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.dortania.opencore-legacy-patcher</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>OpenCore Legacy Patcher</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.10.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2020-2024 Dortania</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSRequiresAquaSystemAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
ci_tooling/update_shim/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher
Executable file
BIN
ci_tooling/update_shim/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher
Executable file
Binary file not shown.
Binary file not shown.
8
ci_tooling/update_shim/README.md
Normal file
8
ci_tooling/update_shim/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# OpenCore-Patcher Update Shim
|
||||
|
||||
To handle the new PKG installation method, old versions of OpenCore Legacy Patcher updating to newer versions will still require 'OpenCore-Patcher.app' to be available for download.
|
||||
|
||||
Thus the goal of this app is to install an embedded PKG under ./OpenCore-Patcher.app/Contents/Resources/OpenCore-Patcher.pkg to handle the update process.
|
||||
|
||||
Source is available at:
|
||||
* https://github.com/dortania/OCLP-Helper
|
||||
@@ -28,7 +28,6 @@ class GlobalEnviromentSettings:
|
||||
|
||||
self._generate_settings_file()
|
||||
self._convert_defaults_to_global_settings()
|
||||
self._fix_file_permission()
|
||||
|
||||
|
||||
def read_property(self, property_name: str) -> str:
|
||||
@@ -105,22 +104,4 @@ class GlobalEnviromentSettings:
|
||||
Path(defaults_path).unlink()
|
||||
except Exception as e:
|
||||
logging.error("Error: Unable to delete defaults plist")
|
||||
logging.error(e)
|
||||
|
||||
|
||||
def _fix_file_permission(self) -> None:
|
||||
"""
|
||||
Fixes file permission for log file
|
||||
|
||||
If OCLP was invoked as root, file permission will only allow root to write to settings file
|
||||
This in turn breaks normal OCLP execution to write to settings file
|
||||
"""
|
||||
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
return
|
||||
|
||||
# Set file permission to allow any user to write to log file
|
||||
result = subprocess_wrapper.run_as_root(["/bin/chmod", "777", self.global_settings_plist], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
logging.warning("Failed to fix settings file permissions:")
|
||||
subprocess_wrapper.log(result)
|
||||
logging.error(e)
|
||||
@@ -92,24 +92,11 @@ class tui_disk_installation:
|
||||
def install_opencore(self, full_disk_identifier: str):
|
||||
# TODO: Apple Script fails in Yosemite(?) and older
|
||||
logging.info(f"Mounting partition: {full_disk_identifier}")
|
||||
if self.constants.detected_os >= os_data.os_data.el_capitan and not self.constants.recovery_status and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
try:
|
||||
applescript.AppleScript(f'''do shell script "diskutil mount {full_disk_identifier}" with prompt "OpenCore Legacy Patcher needs administrator privileges to mount this volume." with administrator privileges without altering line endings''').run()
|
||||
except applescript.ScriptError as e:
|
||||
if "User canceled" in str(e):
|
||||
logging.info("Mount cancelled by user")
|
||||
return
|
||||
logging.info(f"An error occurred: {e}")
|
||||
if utilities.check_boot_mode() == "safe_boot":
|
||||
logging.info("\nSafe Mode detected. FAT32 is unsupported by macOS in this mode.")
|
||||
logging.info("Please disable Safe Mode and try again.")
|
||||
return
|
||||
else:
|
||||
result = subprocess_wrapper.run_as_root(["/usr/sbin/diskutil", "mount", full_disk_identifier], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
logging.info("Mount failed")
|
||||
subprocess_wrapper.log(result)
|
||||
return
|
||||
result = subprocess_wrapper.run_as_root(["/usr/sbin/diskutil", "mount", full_disk_identifier], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
logging.info("Mount failed")
|
||||
subprocess_wrapper.log(result)
|
||||
return
|
||||
|
||||
partition_info = plistlib.loads(subprocess.run(["/usr/sbin/diskutil", "info", "-plist", full_disk_identifier], stdout=subprocess.PIPE).stdout.decode().strip().encode())
|
||||
parent_disk = partition_info["ParentWholeDisk"]
|
||||
|
||||
@@ -464,10 +464,6 @@ class KernelDebugKitObject:
|
||||
if self.passive is True:
|
||||
return
|
||||
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot remove KDK, not running as root")
|
||||
return
|
||||
|
||||
if not Path(kdk_path).exists():
|
||||
logging.warning(f"KDK does not exist: {kdk_path}")
|
||||
return
|
||||
@@ -579,10 +575,6 @@ class KernelDebugKitUtilities:
|
||||
bool: True if successful, False if not
|
||||
"""
|
||||
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot install KDK, not running as root")
|
||||
return False
|
||||
|
||||
logging.info(f"Installing KDK package: {kdk_path.name}")
|
||||
logging.info(f"- This may take a while...")
|
||||
|
||||
@@ -607,10 +599,6 @@ class KernelDebugKitUtilities:
|
||||
bool: True if successful, False if not
|
||||
"""
|
||||
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot install KDK, not running as root")
|
||||
return False
|
||||
|
||||
logging.info(f"Extracting downloaded KDK disk image")
|
||||
with tempfile.TemporaryDirectory() as mount_point:
|
||||
result = subprocess_wrapper.run_as_root(["/usr/bin/hdiutil", "attach", kdk_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
@@ -669,10 +657,6 @@ class KernelDebugKitUtilities:
|
||||
logging.warning("Malformed KDK Info.plist provided, cannot create backup")
|
||||
return
|
||||
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot create KDK backup, not running as root")
|
||||
return
|
||||
|
||||
if not Path(KDK_INSTALL_PATH).exists():
|
||||
subprocess_wrapper.run_as_root(["/bin/mkdir", "-p", KDK_INSTALL_PATH], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ class InitializeLoggingSupport:
|
||||
self._attempt_initialize_logging_configuration()
|
||||
self._start_logging()
|
||||
self._implement_custom_traceback_handler()
|
||||
self._fix_file_permission()
|
||||
self._clean_prior_version_logs()
|
||||
|
||||
|
||||
@@ -123,29 +122,6 @@ class InitializeLoggingSupport:
|
||||
logging.error(f"Failed to delete log file: {e}")
|
||||
|
||||
|
||||
def _fix_file_permission(self) -> None:
|
||||
"""
|
||||
Fixes file permission for log file
|
||||
|
||||
If OCLP was invoked as root, file permission will only allow root to write to log file
|
||||
This in turn breaks normal OCLP execution to write to log file
|
||||
"""
|
||||
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
return
|
||||
|
||||
paths = [
|
||||
self.log_filepath, # ~/Library/Logs/Dortania/OpenCore-Patcher_{version}_{date}.log
|
||||
self.log_filepath.parent, # ~/Library/Logs/Dortania
|
||||
]
|
||||
|
||||
for path in paths:
|
||||
result = subprocess_wrapper.run_as_root(["/bin/chmod", "777", path], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
logging.error(f"Failed to fix log file permissions")
|
||||
subprocess_wrapper.log(result)
|
||||
|
||||
|
||||
def _initialize_logging_configuration(self, log_to_file: bool = True) -> None:
|
||||
"""
|
||||
Initialize logging framework configuration
|
||||
|
||||
@@ -15,7 +15,8 @@ from ..datasets import os_data
|
||||
|
||||
from . import (
|
||||
network_handler,
|
||||
utilities
|
||||
utilities,
|
||||
subprocess_wrapper
|
||||
)
|
||||
|
||||
|
||||
@@ -63,16 +64,10 @@ class InstallerCreation():
|
||||
"""
|
||||
|
||||
logging.info("Extracting macOS installer from InstallAssistant.pkg")
|
||||
try:
|
||||
applescript.AppleScript(
|
||||
f'''do shell script "installer -pkg {Path(download_path)}/InstallAssistant.pkg -target /"'''
|
||||
' with prompt "OpenCore Legacy Patcher needs administrator privileges to extract the installer."'
|
||||
" with administrator privileges"
|
||||
" without altering line endings",
|
||||
).run()
|
||||
except Exception as e:
|
||||
result = subprocess_wrapper.run_as_root(["/usr/sbin/installer", "-pkg", f"{Path(download_path)}/InstallAssistant.pkg", "-target", "/"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to install InstallAssistant")
|
||||
logging.info(f" Error Code: {e}")
|
||||
subprocess_wrapper.log(result)
|
||||
return False
|
||||
|
||||
logging.info("InstallAssistant installed")
|
||||
|
||||
@@ -9,9 +9,6 @@ import logging
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
from functools import cache
|
||||
|
||||
from . import utilities
|
||||
|
||||
|
||||
OCLP_PRIVILEGED_HELPER = "/Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper"
|
||||
@@ -37,17 +34,6 @@ class PrivilegedHelperErrorCodes(enum.IntEnum):
|
||||
OCLP_PHT_ERROR_CATCH_ALL = 170
|
||||
|
||||
|
||||
@cache
|
||||
def supports_privileged_helper() -> bool:
|
||||
"""
|
||||
Check if Privileged Helper Tool is supported.
|
||||
|
||||
When privileged helper is officially shipped, this function should always return True.
|
||||
Something would have gone very wrong if it doesn't exist past that point.
|
||||
"""
|
||||
return Path(OCLP_PRIVILEGED_HELPER).exists()
|
||||
|
||||
|
||||
def run(*args, **kwargs) -> subprocess.CompletedProcess:
|
||||
"""
|
||||
Basic subprocess.run wrapper.
|
||||
@@ -66,14 +52,6 @@ def run_as_root(*args, **kwargs) -> subprocess.CompletedProcess:
|
||||
if not Path(args[0][0]).exists():
|
||||
raise FileNotFoundError(f"File not found: {args[0][0]}")
|
||||
|
||||
if supports_privileged_helper() is False:
|
||||
# Fall back to old logic
|
||||
# This should be removed when we start shipping the helper tool officially
|
||||
if os.getuid() == 0 or utilities.check_cli_args() is not None:
|
||||
return subprocess.run(*args, **kwargs)
|
||||
else:
|
||||
return subprocess.run(["/usr/bin/sudo"] + [args[0][0]] + args[0][1:], **kwargs)
|
||||
|
||||
return subprocess.run([OCLP_PRIVILEGED_HELPER] + [args[0][0]] + args[0][1:], **kwargs)
|
||||
|
||||
|
||||
|
||||
@@ -78,33 +78,6 @@ class CheckBinaryUpdates:
|
||||
|
||||
return first_version > second_version
|
||||
|
||||
def _determine_local_build_type(self) -> str:
|
||||
"""
|
||||
Check if the local build is a GUI or TUI build
|
||||
|
||||
Returns:
|
||||
str: "GUI" or "TUI"
|
||||
"""
|
||||
|
||||
return "GUI" if self.constants.wxpython_variant else "TUI"
|
||||
|
||||
def _determine_remote_type(self, remote_name: str) -> str:
|
||||
"""
|
||||
Check if the remote build is a GUI or TUI build
|
||||
|
||||
Parameters:
|
||||
remote_name (str): Name of the remote build
|
||||
|
||||
Returns:
|
||||
str: "GUI" or "TUI"
|
||||
"""
|
||||
|
||||
if "TUI" in remote_name:
|
||||
return "TUI"
|
||||
elif "GUI" in remote_name:
|
||||
return "GUI"
|
||||
else:
|
||||
return "Unknown"
|
||||
|
||||
def check_binary_updates(self) -> Optional[dict]:
|
||||
"""
|
||||
@@ -143,12 +116,11 @@ class CheckBinaryUpdates:
|
||||
|
||||
for asset in data_set["assets"]:
|
||||
logging.info(f"Found asset: {asset['name']}")
|
||||
if self._determine_remote_type(asset["name"]) == self._determine_local_build_type():
|
||||
if asset["name"] == "OpenCore-Patcher.pkg":
|
||||
self.latest_details = {
|
||||
"Name": asset["name"],
|
||||
"Version": latest_remote_version,
|
||||
"Link": asset["browser_download_url"],
|
||||
"Type": self._determine_remote_type(asset["name"]),
|
||||
"Github Link": f"https://github.com/dortania/OpenCore-Legacy-Patcher/releases/{latest_remote_version}",
|
||||
}
|
||||
return self.latest_details
|
||||
|
||||
@@ -920,13 +920,16 @@ class PatchSysVolume:
|
||||
return
|
||||
|
||||
logging.info("- Verifying whether Root Patching possible")
|
||||
if sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=not self.constants.wxpython_variant) is True:
|
||||
logging.info("- Patcher is capable of patching")
|
||||
if self._check_files():
|
||||
if self._mount_root_vol() is True:
|
||||
self._patch_root_vol()
|
||||
else:
|
||||
logging.info("- Recommend rebooting the machine and trying to patch again")
|
||||
if sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=not self.constants.wxpython_variant) is False:
|
||||
logging.error("- Cannot continue with patching!!!")
|
||||
return
|
||||
|
||||
logging.info("- Patcher is capable of patching")
|
||||
if self._check_files():
|
||||
if self._mount_root_vol() is True:
|
||||
self._patch_root_vol()
|
||||
else:
|
||||
logging.info("- Recommend rebooting the machine and trying to patch again")
|
||||
|
||||
|
||||
def start_unpatch(self) -> None:
|
||||
@@ -935,8 +938,11 @@ class PatchSysVolume:
|
||||
"""
|
||||
|
||||
logging.info("- Starting Unpatch Process")
|
||||
if sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=True) is True:
|
||||
if self._mount_root_vol() is True:
|
||||
self._unpatch_root_vol()
|
||||
else:
|
||||
logging.info("- Recommend rebooting the machine and trying to patch again")
|
||||
if sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=True) is False:
|
||||
logging.error("- Cannot continue with unpatching!!!")
|
||||
return
|
||||
|
||||
if self._mount_root_vol() is True:
|
||||
self._unpatch_root_vol()
|
||||
else:
|
||||
logging.info("- Recommend rebooting the machine and trying to patch again")
|
||||
|
||||
@@ -336,11 +336,7 @@ Please check the Github page for more information about this release."""
|
||||
|
||||
def install_auto_patcher_launch_agent(self, kdk_caching_needed: bool = False):
|
||||
"""
|
||||
Install the Auto Patcher Launch Agent
|
||||
|
||||
Installs the following:
|
||||
- OpenCore-Patcher.app in /Library/Application Support/Dortania/
|
||||
- com.dortania.opencore-legacy-patcher.auto-patch.plist in /Library/LaunchAgents/
|
||||
Install patcher launch services
|
||||
|
||||
See start_auto_patch() comments for more info
|
||||
"""
|
||||
@@ -375,44 +371,6 @@ Please check the Github page for more information about this release."""
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/chmod", "644", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root_and_verify(["/usr/sbin/chown", "root:wheel", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
if self.constants.launcher_binary.startswith("/Library/Application Support/Dortania/"):
|
||||
logging.info("- Skipping Patcher Install, already installed")
|
||||
return
|
||||
|
||||
# Verify our binary isn't located in '/Library/Application Support/Dortania/'
|
||||
# As we'd simply be duplicating ourselves
|
||||
logging.info("- Installing Auto Patcher Launch Agent")
|
||||
|
||||
if not Path("Library/Application Support/Dortania").exists():
|
||||
logging.info("- Creating /Library/Application Support/Dortania/")
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mkdir", "-p", "/Library/Application Support/Dortania"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
logging.info("- Copying OpenCore Patcher to /Library/Application Support/Dortania/")
|
||||
if Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists():
|
||||
logging.info("- Deleting existing OpenCore-Patcher")
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", "-R", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
# Strip everything after OpenCore-Patcher.app
|
||||
path = str(self.constants.launcher_binary).split("/Contents/MacOS/OpenCore-Patcher")[0]
|
||||
logging.info(f"- Copying {path} to /Library/Application Support/Dortania/")
|
||||
subprocess_wrapper.run_as_root_and_verify(["/usr/bin/ditto", path, "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
if not Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists():
|
||||
# Sometimes the binary the user launches may have a suffix (ie. OpenCore-Patcher 3.app)
|
||||
# We'll want to rename it to OpenCore-Patcher.app
|
||||
path = path.split("/")[-1]
|
||||
logging.info(f"- Renaming {path} to OpenCore-Patcher.app")
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mv", f"/Library/Application Support/Dortania/{path}", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
subprocess.run(["/usr/bin/xattr", "-cr", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Making app alias
|
||||
# Simply an easy way for users to notice the app
|
||||
# If there's already an alias or exiting app, skip
|
||||
if not Path("/Applications/OpenCore-Patcher.app").exists():
|
||||
logging.info("- Making app alias")
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _create_rsr_monitor_daemon(self) -> bool:
|
||||
# Get kext list in /Library/Extensions that have the 'GPUCompanionBundles' property
|
||||
|
||||
@@ -33,6 +33,9 @@ class SysPatchMount:
|
||||
If none, failed to mount.
|
||||
"""
|
||||
result = self._mount_root_volume()
|
||||
if result is None:
|
||||
logging.error("Failed to mount root volume")
|
||||
return None
|
||||
if not Path(result).exists():
|
||||
logging.error(f"Attempted to mount root volume, but failed: {result}")
|
||||
return None
|
||||
|
||||
@@ -385,8 +385,8 @@ class macOSInstallerFlashFrame(wx.Frame):
|
||||
with open(self.constants.installer_sh_path, "r") as f:
|
||||
logging.info(f"installer.sh contents:\n{f.read()}")
|
||||
|
||||
args = [self.constants.oclp_helper_path, "/bin/sh", self.constants.installer_sh_path]
|
||||
result = subprocess.run(args, capture_output=True, text=True)
|
||||
args = ["/bin/sh", self.constants.installer_sh_path]
|
||||
result = subprocess_wrapper.run_as_root(args, capture_output=True, text=True)
|
||||
output = result.stdout
|
||||
error = result.stderr if result.stderr else ""
|
||||
|
||||
|
||||
@@ -218,8 +218,6 @@ class MainFrame(wx.Frame):
|
||||
self.on_build_and_install()
|
||||
return
|
||||
|
||||
self._fix_local_install()
|
||||
|
||||
if "--update_installed" in sys.argv and self.constants.has_checked_updates is False and gui_support.CheckProperties(self.constants).host_can_build():
|
||||
# Notify user that the update has been installed
|
||||
self.constants.has_checked_updates = True
|
||||
@@ -251,34 +249,6 @@ class MainFrame(wx.Frame):
|
||||
threading.Thread(target=self._check_for_updates).start()
|
||||
|
||||
|
||||
def _fix_local_install(self) -> None:
|
||||
"""
|
||||
Work-around users manually copying the app to /Applications
|
||||
We'll delete the app, and create a proper symlink
|
||||
Note: This *shouldn't* be needed with installs after 0.6.7, but it's a good catch-all
|
||||
"""
|
||||
|
||||
if "--update_installed" not in sys.argv:
|
||||
return
|
||||
if self.constants.has_checked_updates is True:
|
||||
return
|
||||
|
||||
# Check if app exists in /Applications, and is not a symlink
|
||||
if Path("/Applications/OpenCore-Patcher.app").exists() and Path("/Applications/OpenCore-Patcher.app").is_symlink() is False:
|
||||
logging.info("Found user-installed app in /Applications, replacing with symlink")
|
||||
# Delete app
|
||||
result = subprocess.run(["/bin/rm", "-rf", "/Applications/OpenCore-Patcher.app"], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to delete app from /Applications")
|
||||
return
|
||||
|
||||
# Create symlink
|
||||
result = subprocess.run(["/bin/ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to create symlink to /Applications")
|
||||
return
|
||||
|
||||
|
||||
def _check_for_updates(self):
|
||||
if self.constants.has_checked_updates is True:
|
||||
return
|
||||
|
||||
@@ -1303,7 +1303,7 @@ Hardware Information:
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.parent.GetPosition(),
|
||||
url=f"https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/{branch}/OpenCore-Patcher.app%20%28GUI%29.zip",
|
||||
url=f"https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/{branch}/OpenCore-Patcher.pkg.zip",
|
||||
version_label="(Nightly)"
|
||||
)
|
||||
|
||||
@@ -1325,21 +1325,15 @@ Hardware Information:
|
||||
raise Exception("Test Exception")
|
||||
|
||||
def on_mount_root_vol(self, event: wx.Event) -> None:
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
wx.MessageDialog(self.parent, "Please relaunch as Root to mount the Root Volume", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
#Don't need to pass model as we're bypassing all logic
|
||||
if sys_patch.PatchSysVolume("",self.constants)._mount_root_vol() == True:
|
||||
wx.MessageDialog(self.parent, "Root Volume Mounted, remember to fix permissions before saving the Root Volume", "Success", wx.OK | wx.ICON_INFORMATION).ShowModal()
|
||||
else:
|
||||
#Don't need to pass model as we're bypassing all logic
|
||||
if sys_patch.PatchSysVolume("",self.constants)._mount_root_vol() == True:
|
||||
wx.MessageDialog(self.parent, "Root Volume Mounted, remember to fix permissions before saving the Root Volume", "Success", wx.OK | wx.ICON_INFORMATION).ShowModal()
|
||||
else:
|
||||
wx.MessageDialog(self.parent, "Root Volume Mount Failed, check terminal output", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
wx.MessageDialog(self.parent, "Root Volume Mount Failed, check terminal output", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def on_bless_root_vol(self, event: wx.Event) -> None:
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
wx.MessageDialog(self.parent, "Please relaunch as Root to save changes", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
#Don't need to pass model as we're bypassing all logic
|
||||
if sys_patch.PatchSysVolume("",self.constants)._rebuild_root_volume() == True:
|
||||
wx.MessageDialog(self.parent, "Root Volume saved, please reboot to apply changes", "Success", wx.OK | wx.ICON_INFORMATION).ShowModal()
|
||||
else:
|
||||
#Don't need to pass model as we're bypassing all logic
|
||||
if sys_patch.PatchSysVolume("",self.constants)._rebuild_root_volume() == True:
|
||||
wx.MessageDialog(self.parent, "Root Volume saved, please reboot to apply changes", "Success", wx.OK | wx.ICON_INFORMATION).ShowModal()
|
||||
else:
|
||||
wx.MessageDialog(self.parent, "Root Volume update Failed, check terminal output", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
wx.MessageDialog(self.parent, "Root Volume update Failed, check terminal output", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
@@ -66,20 +66,14 @@ class GenerateMenubar:
|
||||
|
||||
aboutItem = fileMenu.Append(wx.ID_ABOUT, "&About OpenCore Legacy Patcher")
|
||||
fileMenu.AppendSeparator()
|
||||
relaunchItem = fileMenu.Append(wx.ID_ANY, "&Relaunch as Root")
|
||||
fileMenu.AppendSeparator()
|
||||
revealLogItem = fileMenu.Append(wx.ID_ANY, "&Reveal Log File")
|
||||
|
||||
menubar.Append(fileMenu, "&File")
|
||||
self.frame.SetMenuBar(menubar)
|
||||
|
||||
self.frame.Bind(wx.EVT_MENU, lambda event: gui_about.AboutFrame(self.constants), aboutItem)
|
||||
self.frame.Bind(wx.EVT_MENU, lambda event: RelaunchApplicationAsRoot(self.frame, self.constants).relaunch(None), relaunchItem)
|
||||
self.frame.Bind(wx.EVT_MENU, lambda event: subprocess.run(["/usr/bin/open", "--reveal", self.constants.log_filepath]), revealLogItem)
|
||||
|
||||
if os.geteuid() == 0 or subprocess_wrapper.supports_privileged_helper() is True:
|
||||
relaunchItem.Enable(False)
|
||||
|
||||
|
||||
class GaugePulseCallback:
|
||||
"""
|
||||
@@ -297,89 +291,4 @@ class RestartHost:
|
||||
applescript.AppleScript('tell app "loginwindow" to «event aevtrrst»').run()
|
||||
except applescript.ScriptError as e:
|
||||
logging.error(f"Error while trying to reboot: {e}")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class RelaunchApplicationAsRoot:
|
||||
"""
|
||||
Relaunches the application as root
|
||||
"""
|
||||
|
||||
def __init__(self, frame: wx.Frame, global_constants: constants.Constants) -> None:
|
||||
self.constants = global_constants
|
||||
self.frame: wx.Frame = frame
|
||||
|
||||
|
||||
def relaunch(self, event: wx.Event):
|
||||
|
||||
self.dialog = wx.MessageDialog(
|
||||
self.frame,
|
||||
"OpenCore Legacy Patcher needs to relaunch as admin to continue. You will be prompted to enter your password.",
|
||||
"Relaunch as root?",
|
||||
wx.YES_NO | wx.ICON_QUESTION
|
||||
)
|
||||
|
||||
# Show Dialog Box
|
||||
if self.dialog.ShowModal() != wx.ID_YES:
|
||||
logging.info("User cancelled relaunch")
|
||||
return
|
||||
|
||||
timer: int = 5
|
||||
program_arguments: str = ""
|
||||
|
||||
if event:
|
||||
if event.GetEventObject() != wx.Menu:
|
||||
try:
|
||||
if event.GetEventObject().GetLabel() in ["Start Root Patching", "Reinstall Root Patches"]:
|
||||
program_arguments = " --gui_patch"
|
||||
elif event.GetEventObject().GetLabel() == "Revert Root Patches":
|
||||
program_arguments = " --gui_unpatch"
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
if self.constants.launcher_script is None:
|
||||
program_arguments = f"'{self.constants.launcher_binary}'{program_arguments}"
|
||||
else:
|
||||
program_arguments = f"{self.constants.launcher_binary} {self.constants.launcher_script}{program_arguments}"
|
||||
|
||||
# Relaunch as root
|
||||
args = [
|
||||
"/usr/bin/osascript",
|
||||
"-e",
|
||||
f'''do shell script "{program_arguments}"'''
|
||||
' with prompt "OpenCore Legacy Patcher needs administrator privileges to relaunch as admin."'
|
||||
" with administrator privileges"
|
||||
" without altering line endings",
|
||||
]
|
||||
|
||||
self.frame.DestroyChildren()
|
||||
self.frame.SetSize(300, 300)
|
||||
self.frame.Centre()
|
||||
|
||||
# Header
|
||||
header = wx.StaticText(self.frame, label="Relaunching as root", pos=(-1, 5))
|
||||
header.SetFont(font_factory(19, wx.FONTWEIGHT_BOLD))
|
||||
header.Centre(wx.HORIZONTAL)
|
||||
|
||||
# Add count down label
|
||||
countdown_label = wx.StaticText(self.frame, label=f"Closing old process in {timer} seconds", pos=(0, header.GetPosition().y + header.GetSize().height + 3))
|
||||
countdown_label.SetFont(font_factory(13, wx.FONTWEIGHT_NORMAL))
|
||||
countdown_label.Centre(wx.HORIZONTAL)
|
||||
|
||||
# Set size of frame
|
||||
self.frame.SetSize((-1, countdown_label.GetPosition().y + countdown_label.GetSize().height + 40))
|
||||
|
||||
wx.Yield()
|
||||
|
||||
logging.info(f"Relaunching as root with command: {program_arguments}")
|
||||
subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
wx.Yield()
|
||||
countdown_label.SetLabel(f"Closing old process in {timer} seconds")
|
||||
time.sleep(1)
|
||||
timer -= 1
|
||||
if timer == 0:
|
||||
break
|
||||
|
||||
sys.exit(0)
|
||||
sys.exit(0)
|
||||
@@ -13,7 +13,6 @@ from pathlib import Path
|
||||
from .. import constants
|
||||
|
||||
from ..sys_patch import sys_patch_detect
|
||||
from ..support import subprocess_wrapper
|
||||
|
||||
from ..wx_gui import (
|
||||
gui_main_menu,
|
||||
@@ -242,11 +241,6 @@ class SysPatchDisplayFrame(wx.Frame):
|
||||
if can_unpatch is False:
|
||||
revert_button.Disable()
|
||||
|
||||
# Relaunch as root if not root
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
start_button.Bind(wx.EVT_BUTTON, gui_support.RelaunchApplicationAsRoot(frame, self.constants).relaunch)
|
||||
revert_button.Bind(wx.EVT_BUTTON, gui_support.RelaunchApplicationAsRoot(frame, self.constants).relaunch)
|
||||
|
||||
# Set frame size
|
||||
frame.SetSize((-1, return_button.GetPosition().y + return_button.GetSize().height + 15))
|
||||
frame.ShowWindowModal()
|
||||
@@ -259,8 +253,11 @@ class SysPatchDisplayFrame(wx.Frame):
|
||||
global_constants=self.constants,
|
||||
patches=patches,
|
||||
)
|
||||
self.frame_modal.Hide()
|
||||
self.frame_modal.Destroy()
|
||||
self.frame.Hide()
|
||||
self.frame.Destroy()
|
||||
frame.start_root_patching()
|
||||
self.on_return_dismiss() if self.init_with_parent else self.on_return_to_main_menu()
|
||||
|
||||
|
||||
def on_revert_root_patching(self, patches: dict):
|
||||
@@ -270,8 +267,11 @@ class SysPatchDisplayFrame(wx.Frame):
|
||||
global_constants=self.constants,
|
||||
patches=patches,
|
||||
)
|
||||
self.frame_modal.Hide()
|
||||
self.frame_modal.Destroy()
|
||||
self.frame.Hide()
|
||||
self.frame.Destroy()
|
||||
frame.revert_root_patching()
|
||||
self.on_return_dismiss() if self.init_with_parent else self.on_return_to_main_menu()
|
||||
|
||||
|
||||
def on_return_to_main_menu(self, event: wx.Event = None):
|
||||
|
||||
@@ -43,7 +43,7 @@ class UpdateFrame(wx.Frame):
|
||||
|
||||
self.title: str = title
|
||||
self.constants: constants.Constants = global_constants
|
||||
self.application_path = self.constants.payload_path / "OpenCore-Patcher.app"
|
||||
self.pkg_download_path = self.constants.payload_path / "OpenCore-Patcher.pkg"
|
||||
self.screen_location: wx.Point = screen_location
|
||||
if parent:
|
||||
self.parent.Centre()
|
||||
@@ -98,7 +98,8 @@ class UpdateFrame(wx.Frame):
|
||||
download_obj = None
|
||||
def _fetch_update() -> None:
|
||||
nonlocal download_obj
|
||||
download_obj = network_handler.DownloadObject(url, self.constants.payload_path / "OpenCore-Patcher-GUI.app.zip")
|
||||
file_name = "OpenCore-Patcher.pkg.zip" if url.endswith(".zip") else "OpenCore-Patcher.pkg"
|
||||
download_obj = network_handler.DownloadObject(url, self.constants.payload_path / file_name)
|
||||
|
||||
thread = threading.Thread(target=_fetch_update)
|
||||
thread.start()
|
||||
@@ -189,90 +190,37 @@ class UpdateFrame(wx.Frame):
|
||||
def _extract_update(self) -> None:
|
||||
"""
|
||||
Extracts the update
|
||||
|
||||
Logic:
|
||||
- Distributed through GitHub Actions: Requires extraction
|
||||
- Distributed through GitHub Releases: No extraction required
|
||||
"""
|
||||
logging.info("Extracting update")
|
||||
if Path(self.application_path).exists():
|
||||
subprocess.run(["/bin/rm", "-rf", str(self.application_path)])
|
||||
# GitHub Release
|
||||
if not self.url.endswith(".zip"):
|
||||
return
|
||||
|
||||
# Some hell spawn at Github decided to double zip our Github Actions artifacts
|
||||
# So we need to unzip it twice
|
||||
for i in range(2):
|
||||
result = subprocess.run(
|
||||
["/usr/bin/ditto", "-xk", str(self.constants.payload_path / "OpenCore-Patcher-GUI.app.zip"), str(self.constants.payload_path)], capture_output=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
logging.error(f"Failed to extract update.")
|
||||
subprocess_wrapper.log(result)
|
||||
wx.CallAfter(self.progress_bar_animation.stop_pulse)
|
||||
wx.CallAfter(self.progress_bar.SetValue, 0)
|
||||
wx.CallAfter(wx.MessageBox, f"Failed to extract update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR)
|
||||
wx.CallAfter(sys.exit, 1)
|
||||
break
|
||||
logging.info("Extracting nightly update")
|
||||
if Path(self.pkg_download_path).exists():
|
||||
subprocess.run(["/bin/rm", "-rf", str(self.pkg_download_path)])
|
||||
|
||||
if Path(self.application_path).exists():
|
||||
break
|
||||
|
||||
if i == 1:
|
||||
logging.error("Failed to extract update. Error: Update file does not exist")
|
||||
wx.CallAfter(self.progress_bar_animation.stop_pulse)
|
||||
wx.CallAfter(self.progress_bar.SetValue, 0)
|
||||
wx.CallAfter(wx.MessageBox, "Failed to extract update. Error: Update file does not exist", "Critical Error!", wx.OK | wx.ICON_ERROR)
|
||||
wx.CallAfter(sys.exit, 1)
|
||||
break
|
||||
result = subprocess.run(
|
||||
["/usr/bin/ditto", "-xk", str(self.constants.payload_path / "OpenCore-Patcher.pkg.zip"), str(self.constants.payload_path)], capture_output=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
logging.error(f"Failed to extract update.")
|
||||
subprocess_wrapper.log(result)
|
||||
wx.CallAfter(self.progress_bar_animation.stop_pulse)
|
||||
wx.CallAfter(self.progress_bar.SetValue, 0)
|
||||
wx.CallAfter(wx.MessageBox, f"Failed to extract update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR)
|
||||
wx.CallAfter(sys.exit, 1)
|
||||
|
||||
|
||||
def _install_update(self) -> None:
|
||||
"""
|
||||
Installs update to '/Library/Application Support/Dortania/OpenCore-Patcher.app'
|
||||
Install PKG
|
||||
"""
|
||||
logging.info(f"Installing update: {self.application_path}")
|
||||
|
||||
# Create bash script to run as root
|
||||
script = f"""#!/bin/bash
|
||||
# Check if '/Library/Application Support/Dortania' exists
|
||||
if [ ! -d "/Library/Application Support/Dortania" ]; then
|
||||
mkdir -p "/Library/Application Support/Dortania"
|
||||
fi
|
||||
|
||||
# Check if 'OpenCore-Patcher.app' exists
|
||||
if [ -d "/Library/Application Support/Dortania/OpenCore-Patcher.app" ]; then
|
||||
rm -rf "/Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
fi
|
||||
|
||||
if [ -d "/Applications/OpenCore-Patcher.app" ]; then
|
||||
rm -rf "/Applications/OpenCore-Patcher.app"
|
||||
fi
|
||||
|
||||
# Move '/tmp/OpenCore-Patcher.app' to '/Library/Application Support/Dortania'
|
||||
mv "{str(self.application_path)}" "/Library/Application Support/Dortania/OpenCore-Patcher.app"
|
||||
|
||||
# Check if '/Applications/OpenCore-Patcher.app' exists
|
||||
ln -s "/Library/Application Support/Dortania/OpenCore-Patcher.app" "/Applications/OpenCore-Patcher.app"
|
||||
|
||||
# Create update.plist with info about update
|
||||
cat << EOF > "/Library/Application Support/Dortania/update.plist"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>{self.version_label}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>{self.version_label}</string>
|
||||
<key>InstallationDate</key>
|
||||
<date>{datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")}</date>
|
||||
<key>InstallationSource</key>
|
||||
<string>{self.url}</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
"""
|
||||
# Write script to file
|
||||
with open(self.constants.payload_path / "update.sh", "w") as f:
|
||||
f.write(script)
|
||||
|
||||
# Execute script
|
||||
args = [self.constants.oclp_helper_path, "/bin/sh", str(self.constants.payload_path / "update.sh")]
|
||||
result = subprocess.run(args, capture_output=True)
|
||||
logging.info(f"Installing update: {self.pkg_download_path}")
|
||||
result = subprocess_wrapper.run_as_root(["/usr/sbin/installer", "-pkg", str(self.pkg_download_path), "-target", "/"], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
wx.CallAfter(self.progress_bar_animation.stop_pulse)
|
||||
wx.CallAfter(self.progress_bar.SetValue, 0)
|
||||
@@ -282,7 +230,12 @@ EOF
|
||||
else:
|
||||
logging.critical("Failed to install update.")
|
||||
subprocess_wrapper.log(result)
|
||||
wx.CallAfter(wx.MessageBox, f"Failed to install update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR)
|
||||
|
||||
# If it fails, fall back to opening the PKG
|
||||
logging.error("Failed to install update, attempting to open PKG")
|
||||
subprocess.run(["/usr/bin/open", str(self.pkg_download_path)])
|
||||
|
||||
wx.CallAfter(wx.MessageBox, f"Failed to install update. Please try installing the OpenCore-Patcher.pkg manually or download from GitHub", "Critical Error!", wx.OK | wx.ICON_ERROR)
|
||||
wx.CallAfter(sys.exit, 1)
|
||||
|
||||
|
||||
|
||||
@@ -5,4 +5,6 @@ pyinstaller
|
||||
packaging
|
||||
py_sip_xnu
|
||||
py-applescript
|
||||
markdown2
|
||||
markdown2
|
||||
macos-pkg-builder
|
||||
mac-signing-buddy
|
||||
Reference in New Issue
Block a user