diff --git a/.github/workflows/build-app-wxpython.yml b/.github/workflows/build-app-wxpython.yml index f795ef8964..57d66b58c9 100644 --- a/.github/workflows/build-app-wxpython.yml +++ b/.github/workflows/build-app-wxpython.yml @@ -27,9 +27,6 @@ jobs: 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 @@ -37,20 +34,23 @@ jobs: # p12-file-base64: ${{ secrets.MAC_CODESIGN_CERT }} # p12-password: ${{ secrets.MAC_NOTARIZATION_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: Install Dependencies + run: /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 -m pip install -r requirements.txt - - name: Package Binary - run: cd dist; ditto -c -k --sequesterRsrc --keepParent OpenCore-Patcher.app ../OpenCore-Patcher-wxPython.app.zip - - - 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: Build Binary + run: > + /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 Build-Suite.command + --application-signing-identity "${{ env.MAC_CODESIGN_IDENTITY }}" + --notarization-apple-id "${{ env.MAC_NOTARIZATION_USERNAME }}" --notarization-password "${{ env.MAC_NOTARIZATION_PASSWORD }}" --notarization-team-id "${{ env.MAC_NOTARIZATION_TEAM_ID }}" + --git-branch "${{ env.branch }}" --git-commit-url "${{ env.commiturl }}" --git-commit-date "${{ env.commitdate }}" + --reset-dmg-cache --reset-pyinstaller-cache + --analytics-key "${{ env.ANALYTICS_KEY }}" --analytics-endpoint "${{ env.ANALYTICS_SITE }}" - name: Generate support package run: /usr/local/bin/packagesbuild ./ci_tooling/autopkg/AutoPkg-Assets-Setup.pkgproj - 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 diff --git a/Build-Suite.command b/Build-Suite.command new file mode 100644 index 0000000000..539bbcf4fd --- /dev/null +++ b/Build-Suite.command @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +""" +Build-Suite.command: Generate OpenCore-Patcher.app and OpenCore-Patcher.pkg +""" + +import os +import time +import argparse + +from pathlib import Path + +from ci_tooling.build_module import ( + application, + disk_images, + package, + sign_notarize +) + + +def main() -> None: + """ + Parse Command Line Arguments + """ + + parser = argparse.ArgumentParser(description="Build OpenCore Legacy Patcher Suite") + + # Code Signing Parameters + # - Application Signing Identity + # - Installer Signing Identity + 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 + # - Notarization Apple ID + # - Notarization Password + # - Notarization Team ID + 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 + # - Git Branch + # - Git Commit + # - Git Commit Date + 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 + # - Reset payloads.dmg + # - Clean PyInstaller Cache + 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) + + # Analytics Parameters + # - Key + # - Site + parser.add_argument("--analytics-key", type=str, help="Analytics Key", default=None) + parser.add_argument("--analytics-endpoint", type=str, help="Analytics Endpoint", default=None) + + # Parse Arguments + args = parser.parse_args() + + # Set 'Current Working Directory' to script directory + os.chdir(Path(__file__).resolve().parent) + + # Prepare workspace + disk_images.GenerateDiskImages(args.reset_dmg_cache).generate() + + # 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() + + # Build OpenCore-Patcher.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() + + +if __name__ == '__main__': + _start = time.time() + main() + print(f"Build script completed in {str(round(time.time() - _start, 2))} seconds") \ No newline at end of file diff --git a/ci_tooling/autopkg/preinstall.sh b/ci_tooling/autopkg/preinstall.sh index ed2e77f62a..3db2a3c6b6 100755 --- a/ci_tooling/autopkg/preinstall.sh +++ b/ci_tooling/autopkg/preinstall.sh @@ -14,6 +14,7 @@ filesToRemove=( "/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" ) diff --git a/ci_tooling/build_module/application.py b/ci_tooling/build_module/application.py new file mode 100644 index 0000000000..1c154239ea --- /dev/null +++ b/ci_tooling/build_module/application.py @@ -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() diff --git a/ci_tooling/build_module/disk_images.py b/ci_tooling/build_module/disk_images.py new file mode 100644 index 0000000000..d736005b50 --- /dev/null +++ b/ci_tooling/build_module/disk_images.py @@ -0,0 +1,133 @@ + +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() \ No newline at end of file diff --git a/ci_tooling/build_module/package.py b/ci_tooling/build_module/package.py new file mode 100644 index 0000000000..5472a7c3c2 --- /dev/null +++ b/ci_tooling/build_module/package.py @@ -0,0 +1,59 @@ +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", + } + + + def _generate_welcome(self) -> str: + """ + Generate Welcome message for 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(self) -> None: + """ + Generate OpenCore-Patcher.pkg + """ + 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_welcome(), + ).build() is True \ No newline at end of file diff --git a/ci_tooling/build_module/sign_notarize.py b/ci_tooling/build_module/sign_notarize.py new file mode 100644 index 0000000000..b0ce51ea07 --- /dev/null +++ b/ci_tooling/build_module/sign_notarize.py @@ -0,0 +1,41 @@ +import mac_signing_buddy + +from pathlib import Path + + +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}") + 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() diff --git a/ci_tooling/installation_pkg/PkgBackground.png b/ci_tooling/installation_pkg/PkgBackground.png new file mode 100644 index 0000000000..09ada5988e Binary files /dev/null and b/ci_tooling/installation_pkg/PkgBackground.png differ diff --git a/ci_tooling/installation_pkg/postinstall.sh b/ci_tooling/installation_pkg/postinstall.sh new file mode 100644 index 0000000000..617e8435ac --- /dev/null +++ b/ci_tooling/installation_pkg/postinstall.sh @@ -0,0 +1,64 @@ +#!/bin/zsh --no-rcs +# ------------------------------------------------------ +# OpenCore Legacy Patcher PKG Post Install Script +# ------------------------------------------------------ +# Set SUID bit on helper tool, and create app alias. +# ------------------------------------------------------ + + +# 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 $helperPath + _createAlias $mainAppPath $shimAppPath +} + + +# MARK: Main +# --------------------------- + +echo "Starting postinstall script..." +_main \ No newline at end of file diff --git a/ci_tooling/installation_pkg/preinstall.sh b/ci_tooling/installation_pkg/preinstall.sh new file mode 100644 index 0000000000..180412bf1c --- /dev/null +++ b/ci_tooling/installation_pkg/preinstall.sh @@ -0,0 +1,69 @@ +#!/bin/zsh --no-rcs +# ------------------------------------------------------ +# OpenCore Legacy Patcher PKG Preinstall Script +# ------------------------------------------------------ +# Remove old files, and prepare directories. +# ------------------------------------------------------ + + +# 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 $file + _createParentDirectory $file + done +} + + +# MARK: Main +# --------------------------- + +echo "Starting preinstall script..." +_main \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a802b06034..26130fe2c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,6 @@ pyinstaller packaging py_sip_xnu py-applescript -markdown2 \ No newline at end of file +markdown2 +macos-pkg-builder +mac-signing-buddy \ No newline at end of file