Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[flake8]
filename =
*.py
max-line-length = 120
extend-exclude =
.venv/
venv/
3 changes: 3 additions & 0 deletions .github/workflows/ci-macos-broker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ jobs:
- name: Install dependencies
run: brew install cmake

- name: Test macOS installer quarantine handling
run: python3 tests/scripts/test-macos-install.py scripts/macos/install.command

- name: Configure universal build
env:
BUILD_VERSION: ${{ inputs.release_version }}
Expand Down
10 changes: 10 additions & 0 deletions scripts/macos/install.command
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fi
/usr/bin/sudo /bin/rm -rf "${legacy_broker_app}" "${installed_broker_app}" "${installed_control_app}"
/usr/bin/sudo /usr/bin/ditto "${broker_app}" "${installed_broker_app}"
/usr/bin/sudo /usr/bin/ditto "${control_app}" "${installed_control_app}"
/usr/bin/sudo /usr/bin/xattr -dr com.apple.quarantine "${installed_broker_app}" "${installed_control_app}"
/usr/bin/sudo /usr/sbin/chown -R root:wheel "${installed_broker_app}" "${installed_control_app}"
/usr/bin/sudo /bin/chmod -R a+rX "${installed_broker_app}" "${installed_control_app}"
/usr/bin/sudo /bin/chmod -R go-w "${installed_broker_app}" "${installed_control_app}"
Expand All @@ -43,6 +44,7 @@ fi
/usr/bin/sudo /usr/bin/install -m 0644 \
"${image_root}/${launchd_plist}" "${service_plist}"
/usr/bin/sudo /usr/sbin/chown root:wheel "${service_plist}"
Comment thread
neroshooter marked this conversation as resolved.
Resolved
/usr/bin/sudo /usr/bin/xattr -dr com.apple.quarantine "${service_plist}"

if [[ -d "${image_root}/usr/local/include/libvirtualhid" ]]; then
/usr/bin/sudo /usr/bin/ditto "${image_root}/usr/local/include/libvirtualhid" /usr/local/include/libvirtualhid
Expand All @@ -62,6 +64,14 @@ if [[ -d "${image_root}/usr/local/share/licenses/libvirtualhid" ]]; then
/usr/bin/sudo /usr/bin/ditto "${image_root}/usr/local/share/licenses/libvirtualhid" /usr/local/share/licenses/libvirtualhid
fi

for installed_path in /usr/local/include/libvirtualhid /usr/local/lib/libvirtualhid.a \
/usr/local/bin/libvirtualhid-license /usr/local/lib/cmake/libvirtualhid \
/usr/local/share/licenses/libvirtualhid; do
if [[ -e "${installed_path}" ]]; then
/usr/bin/sudo /usr/bin/xattr -dr com.apple.quarantine "${installed_path}"
fi
done

/usr/bin/sudo /bin/launchctl bootstrap system "${service_plist}"
/usr/bin/sudo /bin/launchctl kickstart -k "system/${bundle_id}"
echo 'Virtual HID Broker and Virtual HID Control installed in /Applications.'
Expand Down
78 changes: 78 additions & 0 deletions tests/scripts/test-macos-install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import os
from pathlib import Path
import re
import subprocess
import sys
import tempfile


def run(*args):
return subprocess.run(args, check=True, capture_output=True, text=True).stdout


installer = Path(sys.argv[1]).read_text()
for quarantined in (False, True):
for optional in (False, True):
with tempfile.TemporaryDirectory(prefix="libvirtualhid-install-", dir="/private/tmp") as temp:
root = Path(temp)
image = root / "image with spaces"
destination = root / "installed"
for name in ("VirtualHIDBroker.app", "VirtualHIDControl.app"):
executable = image / name / "Contents/MacOS/example"
executable.parent.mkdir(parents=True)
executable.write_text("fixture")
(image / "dev.lizardbyte.app.libvirtualhid.plist").write_text("fixture")
if optional:
for name in (
"include/libvirtualhid/gamepad.hpp",
"lib/libvirtualhid.a",
"bin/libvirtualhid-license",
"lib/cmake/libvirtualhid/libvirtualhid-config.cmake",
"share/licenses/libvirtualhid/LICENSE.md",
):
path = image / "usr/local" / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("fixture")
run("/usr/bin/xattr", "-wr", "user.review_marker", "preserve", str(image))
if quarantined:
run("/usr/bin/xattr", "-wr", "com.apple.quarantine", "0083;12345678;Safari;test", str(image))
(destination / "Applications").mkdir(parents=True)
(destination / "Library/LaunchDaemons").mkdir(parents=True)
# Redirect fixed installation paths, leaving image-relative payload paths intact.
source = re.sub(
r"(?<!\})/(Applications|Library|usr/local)(?=[/ \"\n])",
lambda match: str(destination) + match.group(),
installer,
)
# Stub only authorization, ownership, signature validation, and launchd.
# File copying, permissions, and extended attributes use the real macOS tools.
stubs = r'''
function /usr/bin/codesign() { return 0; }
function /usr/bin/sudo() {
[[ "$1" != -v ]] || return 0
case "$1" in
/usr/sbin/chown|/usr/bin/codesign) return 0 ;;
/bin/launchctl)
if [[ "$2" == bootstrap ]]; then
if /usr/bin/xattr -lr "$TEST_DESTINATION" | /usr/bin/grep -q com.apple.quarantine; then
echo 'quarantine remains at bootstrap' >&2
return 1
fi
/usr/bin/touch "$TEST_BOOTSTRAPPED"
fi
return 0 ;;
esac
"$@"
}
'''
script = image / "Install libvirtualhid.command"
script.write_text(stubs + source)
marker = root / "bootstrapped"
env = dict(os.environ, TEST_DESTINATION=str(destination), TEST_BOOTSTRAPPED=str(marker))
result = subprocess.run(["/bin/bash", str(script)], env=env, capture_output=True, text=True)
assert result.returncode == 0, (quarantined, optional, result.stderr)
assert marker.exists(), "bootstrap was skipped"
assert "user.review_marker: preserve" in run("/usr/bin/xattr", "-lr", str(destination))
if quarantined:
assert "com.apple.quarantine" in run("/usr/bin/xattr", "-lr", str(image))
print(f"PASS: quarantined={quarantined}, optional_payload={optional}")
Loading