Skip to content

Commit

Permalink
CI: Reset hdiutil on error
Browse files Browse the repository at this point in the history
  • Loading branch information
khronokernel committed Nov 2, 2023
1 parent d2f5863 commit d9796a9
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 2 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
release:
types: [published]

concurrency:
concurrency:
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}

Expand All @@ -25,7 +25,7 @@ jobs:
- name: Tag folder
run: touch Universal-Binaries/.signed
- name: Build DMG
run: ./build-dmg.sh
run: python3 Generate-DMG.command
- name: Generate Checksums
run: shasum -a 256 Universal-Binaries.dmg > sha256sum.txt
- name: Upload Universal Binaries to Artifacts
Expand Down
123 changes: 123 additions & 0 deletions Generate-DMG.command
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""
Build and sign PatcherSupportPkg Disk Image.
Note password encryption required to pass Apple's notarization.
"""

import os
import subprocess

UB_DIRECTORY: str = "Universal-Binaries"
DMG_NAME: str = "Universal-Binaries.dmg"
DMG_VOLNAME: str = "OpenCore Patcher Resources (Root Patching)"
DMG_SIZE: str = "4096"
DMG_FORMAT: str = "UDRO"
DMG_PASSPHRASE: str = "password"
SIGNING_IDENTITY: str = "OpenCore Legacy Patcher Software Signing"


class GenerateDiskImage:

def __init__(self) -> None:
print("Generating DMG")
self._set_working_directory()
self._strip_extended_attributes()
self._remove_ds_store()
self._create_dmg()
self._convert_dmg()
self._sign_dmg()
self._remove_tmp_dmg()


def _set_working_directory(self) -> None:
print(" - Setting working directory")
os.chdir(os.path.dirname(os.path.realpath(__file__)))


def _reset_hdiutil(self) -> None:
"""
Attempt to reset hdiutil
On some instances, "hdiutil: create failed - Resource busy" is thrown
"""
print(" - Resetting hdiutil")
subprocess.run(["killall", "hdiutil"], capture_output=True)


def _strip_extended_attributes(self) -> None:
print(" - Stripping extended attributes")
subprocess.run(["/usr/bin/xattr", "-rc", UB_DIRECTORY], capture_output=True)


def _remove_ds_store(self) -> None:
print(" - Removing .DS_Store files")
subprocess.run(["find", UB_DIRECTORY, "-name", ".DS_Store", "-delete"], capture_output=True)


def _create_dmg(self, raise_on_error: bool = False) -> None:
"""
raise_on_error: If false, attempts to reset hdiutil and try again
"""
print(" - Creating DMG")
result = subprocess.run([
"hdiutil", "create",
"-srcfolder", UB_DIRECTORY, "tmp.dmg",
"-volname", DMG_VOLNAME,
"-fs", "HFS+", "-ov",
"-format", DMG_FORMAT,
"-megabytes", DMG_SIZE
], capture_output=True)
if result.returncode != 0:
print(" - Failed to create DMG")
print(f"STDOUT:\n{result.stdout.decode('utf-8')}")
print(f"STDERR:\n{result.stderr.decode('utf-8')}")
if raise_on_error:
raise Exception("Failed to create DMG")
if "Resource busy" in result.stderr.decode("utf-8"):
self._reset_hdiutil()
self._create_dmg(raise_on_error=True)
else:
raise Exception("Failed to create DMG")


def _convert_dmg(self, raise_on_error: bool = False) -> None:
"""
raise_on_error: If false, attempts to reset hdiutil and try again
"""
print(" - Converting DMG")
result = subprocess.run([
"hdiutil", "convert",
"-format", "ULMO", "tmp.dmg",
"-o", DMG_NAME,
"-passphrase", DMG_PASSPHRASE,
"-encryption", "-ov"
], capture_output=True)
if result.returncode != 0:
print(" - Failed to convert DMG")
print(f"STDOUT:\n{result.stdout.decode('utf-8')}")
print(f"STDERR:\n{result.stderr.decode('utf-8')}")
if raise_on_error:
raise Exception(f"Failed to convert DMG")
if "Resource busy" in result.stderr.decode("utf-8"):
self._reset_hdiutil()
self._convert_dmg(raise_on_error=True)
else:
raise Exception("Failed to convert DMG")


def _sign_dmg(self) -> None:
print(" - Signing DMG")
result = subprocess.run([
"codesign", "-s", SIGNING_IDENTITY, DMG_NAME
], capture_output=True)
if result.returncode != 0:
print(" - Failed to sign DMG")
print(f"STDOUT:\n{result.stdout.decode('utf-8')}")
print(f"STDERR:\n{result.stderr.decode('utf-8')}")
raise Exception("Failed to sign DMG")


def _remove_tmp_dmg(self) -> None:
subprocess.run(["rm", "tmp.dmg"], capture_output=True)


if __name__ == "__main__":
GenerateDiskImage()

0 comments on commit d9796a9

Please sign in to comment.