Portable Preferences PoC #27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Portable Preferences PoC | |
| on: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| poc: | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [macos-14, macos-15, macos-26] | |
| steps: | |
| ## Environment setup | |
| - uses: actions/checkout@v6 | |
| - name: Setup OS for VO automation | |
| uses: guidepup/setup-action@0.21.0 | |
| ## Example global defaults | |
| - name: Disable Splash | |
| run: | | |
| defaults write com.apple.VoiceOverTraining doNotShowSplashScreen -bool true || true | |
| - name: Enable AppleScript Control | |
| run: | | |
| defaults write com.apple.VoiceOver4/default SCREnableAppleScript -bool true || true | |
| - name: Set Default Greeting | |
| run: | | |
| defaults write com.apple.VoiceOver4/default loginGreeting -string "Default Greeting" || true | |
| - name: Open & Close VoiceOver VoiceOver Utility | |
| run: | | |
| mkdir -p artifacts || true | |
| /usr/bin/osascript start-vo-utility.applescript || true | |
| sleep 5 | |
| screencapture artifacts/vo-utility-pre-portable.png || true | |
| /usr/bin/osascript stop-vo-utility.applescript || true | |
| - name: Start & Stop VoiceOver | |
| run: | | |
| /System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter & | |
| sleep 5 | |
| mkdir -p artifacts || true | |
| screencapture artifacts/started.png || true | |
| kill -15 $(ps aux | egrep "[V]oiceOver.app/Contents/MacOS/VoiceOver launchd -s" | awk '{print $2}') || true | |
| sleep 5 | |
| /usr/bin/osascript stop-vo.applescript || true | |
| sleep 5 | |
| - name: Group Containers List | |
| run: | | |
| ls -la "$HOME/Library/Group Containers/" || true | |
| ls -laR "$HOME/Library/Group Containers/group.com.apple.VoiceOver/" || true | |
| plutil -convert xml1 -o - "$HOME/Library/Group Containers/group.com.apple.VoiceOver/Library/Preferences/com.apple.VoiceOver4.local.plist" || true | |
| ## Automated portable preferences enablement | |
| - name: Convert plist xml to binary | |
| run: | | |
| plutil -convert binary1 "./${{ matrix.os }}/VoiceOver/VoiceOver4.portable/default.plist" | |
| plutil -convert binary1 "./${{ matrix.os }}/VoiceOver/VoiceOver4.portable/master.plist" | |
| - name: Read portable identifier | |
| id: portable | |
| run: | | |
| IDENTIFIER=$( | |
| /usr/libexec/PlistBuddy \ | |
| -c "Print :identifier" \ | |
| "./${{ matrix.os }}/VoiceOver/VoiceOver4.portable/voidentifier" | |
| ) | |
| echo "$IDENTIFIER" | |
| echo "identifier=$IDENTIFIER" >> "$GITHUB_OUTPUT" | |
| - name: Trust portable preferences - group container preferences | |
| env: | |
| IDENTIFIER: ${{ steps.portable.outputs.identifier }} | |
| run: | | |
| PLIST="$HOME/Library/Group Containers/group.com.apple.VoiceOver/Library/Preferences/com.apple.VoiceOver4.local.plist" | |
| # SCRCUserDefaultsAllowAirPlay | |
| /usr/libexec/PlistBuddy -c "Delete :SCRCUserDefaultsAllowAirPlay" "$PLIST" 2>/dev/null || true | |
| /usr/libexec/PlistBuddy -c "Add :SCRCUserDefaultsAllowAirPlay bool true" "$PLIST" | |
| # lastTouchedKey | |
| /usr/libexec/PlistBuddy -c "Delete :lastTouchedKey" "$PLIST" 2>/dev/null || true | |
| /usr/libexec/PlistBuddy -c "Add :lastTouchedKey string $IDENTIFIER" "$PLIST" | |
| # SCRCUserDefaultsAlwaysUsePortableIDs | |
| /usr/libexec/PlistBuddy -c "Print :SCRCUserDefaultsAlwaysUsePortableIDs" "$PLIST" >/dev/null 2>&1 || { | |
| /usr/libexec/PlistBuddy -c "Add :SCRCUserDefaultsAlwaysUsePortableIDs array" "$PLIST" | |
| } | |
| # portablePreferenceKeys | |
| /usr/libexec/PlistBuddy -c "Print :portablePreferenceKeys" "$PLIST" >/dev/null 2>&1 || { | |
| /usr/libexec/PlistBuddy -c "Add :portablePreferenceKeys array" "$PLIST" | |
| } | |
| # append if missing | |
| for KEY in \ | |
| SCRCUserDefaultsAlwaysUsePortableIDs \ | |
| portablePreferenceKeys | |
| do | |
| if ! /usr/libexec/PlistBuddy -c "Print :$KEY" "$PLIST" | grep -Fxq "$IDENTIFIER"; then | |
| INDEX=$( | |
| /usr/libexec/PlistBuddy -c "Print :$KEY" "$PLIST" | | |
| tail -n +2 | | |
| wc -l | | |
| tr -d ' ' | |
| ) | |
| /usr/libexec/PlistBuddy -c "Add :$KEY:$INDEX string $IDENTIFIER" "$PLIST" | |
| fi | |
| done | |
| plutil -convert xml1 -o - "$PLIST" || true | |
| - name: Create portable preferences symlinks | |
| run: | | |
| VO_PORTABLE="/Volumes/GuidepupVoiceOverPreferences/VoiceOver/VoiceOver4.portable" | |
| GROUP_CONTAINER_PREFS="$HOME/Library/Group Containers/group.com.apple.VoiceOver/Library/Preferences" | |
| ln -sf "$VO_PORTABLE" "$GROUP_CONTAINER_PREFS/com.apple.VoiceOver4.portable.scrd" | |
| ln -sf "$VO_PORTABLE" "$GROUP_CONTAINER_PREFS/com.apple.VoiceOver4.portable.scrd.vou" | |
| ln -sf "$VO_PORTABLE" "$GROUP_CONTAINER_PREFS/com.apple.VoiceOver4.portable.scro" | |
| ln -sf "$VO_PORTABLE" "$GROUP_CONTAINER_PREFS/com.apple.VoiceOver4.portable.scui" | |
| - name: Create Volume | |
| run: | | |
| hdiutil create \ | |
| -fs APFS \ | |
| -volname GuidepupVoiceOverPreferences \ | |
| -srcfolder "./${{ matrix.os }}" \ | |
| -format UDRW \ | |
| -ov \ | |
| GuidepupVoiceOverPreferences.dmg | |
| hdiutil attach \ | |
| GuidepupVoiceOverPreferences.dmg \ | |
| -mountpoint /Volumes/GuidepupVoiceOverPreferences | |
| - name: Verify mount | |
| run: | | |
| ls -R /Volumes/GuidepupVoiceOverPreferences | |
| ## Verification Stages | |
| - name: Launch & Verify VoiceOver Welcome Greeting | |
| run: | | |
| mkdir -p artifacts || true | |
| # Start VoiceOver | |
| /System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter & | |
| for i in {1..20}; do | |
| echo "Attempt: $i" | |
| screencapture artifacts/vo-$i.png || true | |
| ACTUAL=$(/usr/bin/osascript voiceOverPerform.applescript 2>/dev/null || true) | |
| echo "Spoken Phrase: $ACTUAL" | |
| sleep 0.01 | |
| done | |
| kill -15 $(ps aux | egrep "[V]oiceOver.app/Contents/MacOS/VoiceOver launchd -s" | awk '{print $2}') || true | |
| sleep 5 | |
| /usr/bin/osascript stop-vo.applescript || true | |
| sleep 5 | |
| - name: Open & Close VoiceOver VoiceOver Utility | |
| run: | | |
| mkdir -p artifacts || true | |
| /usr/bin/osascript start-vo-utility.applescript || true | |
| sleep 5 | |
| screencapture artifacts/vo-utility-end.png || true | |
| /usr/bin/osascript stop-vo-utility.applescript || true | |
| - name: Group Containers List | |
| run: | | |
| ls -la "$HOME/Library/Group Containers/" || true | |
| ls -laR "$HOME/Library/Group Containers/group.com.apple.VoiceOver/" || true | |
| plutil -convert xml1 -o - "$HOME/Library/Group Containers/group.com.apple.VoiceOver/Library/Preferences/com.apple.VoiceOver4.local.plist" || true | |
| ## Cleanup | |
| - name: Cleanup | |
| if: always() | |
| run: | | |
| hdiutil detach /Volumes/GuidepupVoiceOverPreferences || true | |
| rm -f GuidepupVoiceOverPreferences.dmg || true | |
| - uses: actions/upload-artifact@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| name: artifacts-${{ matrix.os }} | |
| path: | | |
| **/artifacts/**/* |