Homebrew-cask: Applescript unavailability prevents whole actions from running

Created on 20 Feb 2016  Â·  18Comments  Â·  Source: Homebrew/homebrew-cask

Actions that require AppleScript (like quitting apps) don't always work and so I have to re-run them sometimes, in addition, it is impossible to uninstall apps that use :login_item over SSH because the System Events app cannot be started over SSH.

Is there a flag that makes it possible to bypass AppleScript actions?

reference: Add support for uninstalling login items #15740

bug

All 18 comments

We only use AppleScript when absolutely necessary. This means that we have no other practical way to run those actions. Removing those is then not desirable, because if you can run them, you will (better than not having them) but if you can’t, you won’t (same as not having them).

I don’t think we can do anything to solve your problem. Will leave open for other maintainers to comment if appropriate.

"if you can’t, you won’t (same as not having them)."

Are you saying that if the AppleScript cannot be run, it is skipped? The behavior I'm observing is that if it cannot be run, the entire action (like uninstalling) is aborted.

Perhaps there should be a sanity check (like whether the current session is a gui session) before running AppleScripts.

Related note: I feel like the AppleScripts sometimes try to be too smart. For example, quitting apps doesnt even make sense for my setup because I have multiple users on my machine, and the AppleScripts only quits the app for the current user.

Are you saying that if the AppleScript cannot be run, it is skipped? The behaviour I'm observing is that if it cannot be run, the entire action (like uninstalling) is aborted.

Ah, I misunderstood the core issue, then. Changed title to better reflect the problem. In that case, that’s what we _should_ do.

quitting apps doesnt even make sense for my setup because I have multiple users on my machine, and the AppleScripts only quits the app for the current user.

I’d argue you’re the minority, there, and unlike the whole action not running, that isn’t severe. We don’t really want to start asking for your password every time you want to uninstall an app, just so we can quit it.

Thanks for updating the title!

Here's some more thoughts: Even when running in GUI, AppleScript actions sometimes fail unexpectedly. I think that in general, AppleScript actions should be treated as nondeterministic – they may work and they may not, and it's impossible to 100% be sure via AppleScript. Perhaps they can be rewritten so that the resulting condition can be verified through non-AppleScript means.

For example, a way to check whether an app is running without AppleScript is to use launchd.
(launchctl list | grep com.apple.Terminal helps tell you whether Terminal is running.) Perhaps the "quit" action be be rewritten as follows:

  • while launchctl list | grep com.apple.Terminal has results

    • ask "Terminal is still running. Quit it now?" [quit and continue] [skip and continue] [abort]

    • print "Attempting to quit Terminal"

    • try osascript -e 'quit app id "com.apple.Terminal"'

    • wait for some delay

Here's a simplified version in case the above tries to do too much:

  • if launchctl list | grep com.apple.Terminal has results

    • try osascript -e 'quit app id "com.apple.Terminal"'

    • wait for some delay

    • assert not launchctl list | grep com.apple.Terminal has results

Here are some benefits of this:

  • If the user has already quit the app, the AppleScript isn't even attempted.
  • We actually verify whether the app ended up being quit.
  • This avoids hacks like the sanity check I mentioned above. The sanity check was a hack because we don't really care whether the session is GUI, we only care whether we need to and/or can quit the app. This approach answers that question directly.

Even if AppleScript actions are skipped on fail, I still have two minor issues against them. These are not that important short-term (although I think they may be important for the longer term) nor are they really actionable, I just wanted to make the maintainers aware of these concerns for future development. I also had a hard time wording them, sorry about that.

1. Assumes single-user machine

I have multiple users on my machine

I’d argue you’re the minority

I'd agree that most HBC users use single-user machines, but I think that this is because this is already a deep underlying assumption of HBC, which discourages multi-user machines from using HBC.

If the maintainers have decided that this HBC has already crossed the point of no return on this issue, then this is fine. However, if there is a chance for HBC to be multiuser-aware in the future, it might be solve problems without this assumption.

2. Tries to be too smart without adequate knowledge

It's pretty cool that HBC tries to take care of things like quitting apps when you uninstall them and such. However, right now, not every such case is taken care of. For example, on upgrade, the old version of an app isn't quit or deleted; in fact, HBC will forget that the old version even exists. In addition, there is no code to handle the case that if on uninstall, the running app refuses to quit.

The fact that HBC automates some things and not others can come across as confusing to users, since it is hard to remember its exact behavior. ("Oh, I don't have to quit the app when I uninstall, but I have to quit the app when I upgrade.") I assume this will be less of a problem as HBC becomes smarter.

I just want to mention that I've observed a bug on OS X 10.10+ where the AppleScript system is totally unusable. You can't even run beep. I maintain an app that uses some AppleScript and the number of customer reports of these failures have been rising in newer OS versions. I have found that creating a new user resolves the issue, but apart from that, a re-install is the one way to resolve it entirely. I love AppleScript, but it's not as stable as it once was.

@bumper314 Have you notified Apple of that bug?

I'm also running into this problem (see #46936). As a security precaution I run my primary user as a non-admin, only logging into the admin account when necessary.

I can think of a couple of workarounds:

  1. Try to detect up-front whether the current brew user has an active WindowServer session, skipping actions which require it if not. Warn the user beforehand.
  2. Wrap those aforementioned actions in a try/catch instead - though this would also skip on other potentially undesirable failures.
  3. Add a command line flag which explicitly disables any actions requiring UI access.

I think 1. would be preferable.

Seems to be vorse on Mojave, see #51945.

Now that I think about it, there’s a chance we’ll get into problems with AppleScript and Mojave. The new security model is bound to break the AppleScript actions for quite a few users. We should think about replacing what we can with non-AppleScript alternatives. uninstall quit: for example, can be replaced entirely with signal:; we’re removing the app anyway, so there’s not a lot of incentive to make sure it exits cleanly.

Pinging @Homebrew/cask since this issue will potentially become bigger than when this issue was opened.

we’re removing the app anyway, so there’s not a lot of incentive to make sure it exits cleanly.

It’s still important to care about user data integrity. An app may need time to save user data on exit so we still need to be somewhat careful with signals such as HUP and KILL.

You still have a valid and important point with everything else you wrote. I believe a TERM signal should be roughly equivalent to sending a quit AppleEvent. We just need to figure out a decent timeout and I don’t really know how AppleScript figures that out.

We just need to figure out a decent timeout and I don’t really know how AppleScript figures that out.

Can’t test now, but I think that after you send a signal it returns immediately, and when you tell AppleScript to quit it will only return after the app exits, so it does check for something.

Possible solution for us: get rid of signal and make quit do:

  1. Send the lowest signal we haven’t tried yet.
  2. Wait 3 seconds.
  3. Check if app is running.
    3.1. If no, return, we’re done.
    3.2. If yes, go back to 1.

That would make building a cask easier and nicer, even. No point in having contributors try different signals if we can escalate them ourselves.

AppleScript quit blocks until the app quits, which might never happen.

You can wrap the quit call in a timeout like this:

with timeout of 10 seconds
  quit application "Foo"
end timeout

But that just throws an exception if the timeout is hit, so you need a try/catch too:

try
  with timeout of 10 seconds
    quit application "Foo"
  end timeout
on error errMsg number errNum
  -- errNum -1712 if timeout
  -- errNum -609 if app crashes while trying to quit
end try

Oddly, while testing this, I found that a low timeout of 1-2 secs would sometimes fail, so the timeout would never occur.

@bumper314 That’s a useful technique. Though it won’t apply if the goal is to replace AppleScript with something else.

If we want to satisfy the following constraints…

  • We don't know how long the app will take to quit
  • We don't know if the AppleScript will hang for other reasons like sandboxing
  • We don't want to run AppleScript unless necessary
  • We want HBC users upgrading to 10.14 to be minimally confused

…here's a solution:

  • while launchctl list | grep com.apple.Terminal has results

    • ask "Terminal is still running. Quit it now?" [quit and continue] [skip and continue] [abort]

    • print "Attempting to quit Terminal"

    • try osascript -e 'quit app id "com.apple.Terminal"'

    • wait for some delay

_A sidenote to everyone who joined this conversation in 2018: You can find the above solution if you scroll up far enough. I recommend reading the earlier comment, as I still think almost all of it is just as relevant today (or more relevant in light of 10.14 changes)._


If we want to satisfy these alternative constraints…

  • We don't know how long the app will take to quit
  • We don't know if the AppleScript will hang for other reasons like sandboxing
  • For pre-10.14 users (and 10.14 users after probably some initial setup), HBC "just works"

…here's a slightly modified solution:

  • while launchctl list | grep com.apple.Terminal has results

    • print "Attempting to quit Terminal"

    • try osascript -e 'quit app id "com.apple.Terminal"'

    • wait for some delay

    • ask "Terminal is still running. Quit it now?" [quit and continue] [skip and continue] [abort]


A note regarding adding a "Quit it now?" prompt. I'm aware that creating a prompt requiring user interaction should not be taken lightly. I am leaving it there to represent a decision _someone_ has to make, and it doesn't have to be a user interaction. We can allow user interaction and make it the user's choice. Or we can make a default choice for them. By the way, the current implementation does already make an implicit choice, the choice is [abort], and that is the reason this thread exists.

I have a multi-user setup, is there anyway to fix this issue? I am having trouble uninstalling casks.

Anyone with this issue please check if it’s solved after https://github.com/Homebrew/brew/pull/6449 is merged and on a brew release.

No replies, so considering this solved.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jrwiegand picture jrwiegand  Â·  3Comments

pendolf picture pendolf  Â·  4Comments

CoolTomatos picture CoolTomatos  Â·  3Comments

pablopunk picture pablopunk  Â·  3Comments

arnogues picture arnogues  Â·  4Comments