Enabling Remote Automation in Safari 14+ via the Command Line

Enabling Remote Automation in Safari 14+ via the Command Line

Along with achieving CircleCI support!

ยท

5 min read

Apple is seemingly on a crusade to make working with their products and tools on the command line increasingly difficult. With each major version release, it appears that more and more useful "power user" features are disappearing, or becoming locked away behind extra permissions.

For the vast majority of users, even developers, this is not an issue as who doesn't like extra security - simply click a few buttons instead and you'll be on your way. Yet little thought is given to headless environments, such as those used in continuous integration and delivery! CI/CD is a huge part of development lifecycles these days and with Apple removing useful features silently becoming a common occurrence, it is making setting up CI/CD pipelines harder than ever.

One such instance is the removal of the AllowRemoteAutomation key in Safari 14 and above. In the blog I discuss how I worked around this to provide our customers at CircleCI the ability to keep testing their websites with safaridriver in macOS Big Sur based images.

Testing with safaridriver on Safari 14+ ๐Ÿ–ฅ

It is essential that the AllowRemoteAutomation key is set to true before safaridriver is enabled, otherwise remote control of Safari is not possible.

In Safari 13 and lower, on CircleCI we could typically set up a job as follows:

- run: defaults write com.apple.Safari AllowRemoteAutomation 1
- run: sudo safaridriver --enable

This was all that was needed to set up the environment ready for Selenium tests.

However, try this on a CircleCI image that provides Safari 14 (such as the Xcode 12.5 image based on macOS Big Sur) and you will be treated with the following error message:

SessionNotCreatedError: Could not create a session: You must enable the 'Allow Remote Automation' option in Safari's Develop menu to control Safari via WebDriver.

Turns out Apple silently removed this key, along with IncludeDevelopMenu and IncludeDebugMenu - all of which were very useful in headless environments! There is seemingly no replacement for these either, so you are now forced to enable them with the GUI. This is also true in Safari 15 on macOS Monterey.

How it feels to be a Mac person working in DevOps

Above: How it feels to be a Mac person working in DevOps (๐Ÿผ=๐ŸŽ)

AppleScript to the Rescue! ๐ŸŽ๐Ÿ“

Thankfully all is not lost as we can employ AppleScript to interact with the GUI for us and enable options we need. AppleScript is a powerful tool for automating actions in macOS, although to get it running in a CI/CD environment

tell application "System Events"
    tell application "Safari" to activate
    delay 5

    tell process "Safari"
        set frontmost to true
        click menu item "Preferencesโ€ฆ" of menu 1 of menu bar item "Safari" of menu bar 1
        click button "Advanced" of toolbar 1 of window 1
        tell checkbox "Show Develop menu in menu bar" of group 1 of group 1 of window 1
            if value is 0 then click it
        end tell
        click button 1 of window 1
        click menu item "Allow Remote Automation" of menu 1 of menu bar item "Develop" of menu bar 1
    end tell
end tell

(You'll have to forgive the formatting here as Hashnode does not know what AppleScript is!)

This script, while it may not look pretty, achieves the same result as toggling the AllowRemoteAutomation key by working with the GUI in Safari to toggle the same option.

Firstly, we need to open Safari and ensure it is brought to the foreground to prevent any potential hiccups when the rest of the script tries to run.

Next we open Safari's preferences, head over to the "Advanced" section and toggle the "Show Develop menu in menu bar" option. This places Safari in Developer mode, equivalent to toggling the IncludeDevelopMenu flag in Safari 13 and lower.

Finally, the script toggles the "Allow Remote Automation" option in the "Develop" menu.

Phew! Now while this works locally (simply throw it into Script Editor and run it), there are more things to consider when getting it to run within the CircleCI macOS environment...

Getting it to Work on CircleCI ๐Ÿค”

While running the above AppleScript locally, you may have noticed a permission pop-up occur. If you do not grant this permission, then the AppleScript will not be able to interact with Safari. As the CircleCI environment is headless and non-interactive, this poses a big problem as, surprise ๐ŸŽ‰, there is no command line equivalent for accepting/adding permissions.

Working with permissions in the CircleCI macOS environment is a whole blog post by itself, so I will not go into depth here (stay tuned for that post in the future). The short story is that we can manually inject the correct permissions into the permissions database (SQLite-based). This emulates what the permissions GUI does, but is only possible if System Integrity Protection is disabled.

Finally, this needs to be made easy for customers to set up. No one wants to copy and paste a bunch of complex sqlite3 commands and add the AppleScript as a file in their repo. This is where Orbs come in. We can package these commands up into a tidy, single line call to the Orb. By using the osascript -e command we can keep the AppleScript in-line to the Orb as well!

For the full source code, check out the add-safari-permissions command in the macOS Orb.

Summary

Well that's it for this blog post, I hope it was helpful!

While Safari is a small drop in the ocean compared to Chrome, with 10% market share, it is still important that developers are able to test against it with ease in their CircleCI pipelines with a bit of AppleScript magic.

I will be writing more about how we have worked around Apple's weirdness in future posts, so please stay tuned.

ย