Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uninstall keyword for pkg-based casks #661

Closed
phinze opened this issue Jul 4, 2013 · 8 comments
Closed

uninstall keyword for pkg-based casks #661

phinze opened this issue Jul 4, 2013 · 8 comments

Comments

@phinze
Copy link
Contributor

phinze commented Jul 4, 2013

I don't think I ever opened this issue as I promised I was going to in #14 so here it is.

There are a few tricky details about this. Namely, some uninstallers require input, some require arguments. We don't get as consistent of an interface as we do with pkg files, so the uninstall keyword interface will have to have some options to support this.

I've got a good first-pass nearly done. So we'll track that here.

@vitorgalvao
Copy link
Member

Does that mean the contributor will have to use different options depending on how the uninstaller works? That worries me a bit, the extra complexity, specially since it’s added to both the main codebase and the contributed code.

I certainly understand the feeling of wanting everything to work in the best way possible, and trying to cover every base, but sometimes it just does not work, and we’re learning the hard way that a lot of apps have specific use cases and we can’t cater to them all. If some of them need some sub-optimal treatment (compared to the more “standard” ones, as it will still be better than doing it manually), perhaps we should try to preserve some sanity (both in code and in mind), and follow the simpler route.

Realistically speaking, homebrew-cask doesn’t need to work exclusively on the command-line, as we’re mostly concerned with GUI apps anyway. In that sense, if some apps have edge cases for uninstall (and even for install, not using a pkg), we could simply take care of the majority of casks as best as we can, and call those few others with edge cases via the system’s open command (essentially the same as opening it manually, but with less trouble).

Naturally, in the end it all depends on the actual implementation and the way that different apps integrate different uninstallers, so it’s a concern for when it lands and there are some reactions.

@phinze
Copy link
Contributor Author

phinze commented Jul 4, 2013

To help make this conversation more concrete. Here's the draft implementation of uninstall, which adds two options :args and input to support VirtualBox and Vagrant uninstall use cases respectively.

3cf0429

we’re learning the hard way that a lot of apps have specific use cases and we can’t cater to them all

This to me was the whole point of going with an executable definition of a Cask - to acknowledge that in the wild apps are going to have random things that are weird and to provide points to be able to capture that weirdness. This is the same thing homebrew does for compiled apps with features like patches and caveats and IMHO it's very much a part of our project's mission to support stuff that's out of the ordinary in a relatively sane way. If we try to impose a strict schema on the apps we deal with, we're never going to be able to capture everything - most especially the exceptions. And to me that's one of the most valuable things we can do - to document and automate the weird cases.

Realistically speaking, homebrew-cask doesn’t need to work exclusively on the command-line

I whole-heartedly disagree with this. homebrew-cask is a unix-philosophy-based tool and needs to be automateable itself. Integrating it with a tool like Boxen would become totally useless if there were cases that required user input. In my view we are trying to be as close as we can get to a package manager.

@vitorgalvao
Copy link
Member

I was referring to cases like (to take from the actual code) :input => %w[Yes] and :args => %w[--unattended]. My concern was that uninstallers can be the craziest thing we’ll have to deal with, as they can be implemented in extremely different ways from across the board — there’s no standard filetype or way to go about it, it’s a free-for-all.

Since, judging from the pull requests we already have, it seems the most common way to do it is via an app file (that we’ll need to open anyway), and command-line tools (like the ones Vagrant and Virtualbox use) are the exception, I was wondering if we should be catering to those edge-cases by implementing specific options for them, since we can just as well call the uninstall file (that in the case of Vagrant and Virtualbox would open a terminal) and let it ask the questions it needs, so we’d have a somewhat universal implementation (works with any uninstaller, no matter the filetype or options it needs, out of the box). Yes, it does take an extra step to complete on those particular cases, but one implementation works every time, and the casks can remain in their (mostly) standard no-inline-code state.

@phinze
Copy link
Contributor Author

phinze commented Jul 4, 2013

I really don't want to have homebrew-cask popping up windows that users have to interact with. To me the whole motivation of the project was from being sick of having to click around to manage my apps.

Can you point me to the examples of app based uninstallers? Honestly for me personally VirtualBox and Vagrant are the most important two pkg-based apps, and I want to take a sample of the other ones you're talking about to see if they're internally calling scripts or actually running binary tasks. I want to get a feel for what "normal" is if these are edge cases.

there’s no standard filetype or way to go about it, it’s a free-for-all.

Before this project, you could say the same thing about app downloads too. :)

My inclination is to try it this way for a bit and then see how bad it gets.

I cleaned up the branch implementation here. It now works properly for install/uninstall with both vagrant and virtualbox.

ddb1fea

@vitorgalvao
Copy link
Member

Only now did I see the edits to the previous post, so I’ll answer them here, in succession (this extended the post quite a bit).

This to me was the whole point of going with an executable definition of a Cask (…) And to me that's one of the most valuable things we can do - to document and automate the weird cases.

I stand corrected, and completely agree with this (the whole paragraph, really). However, that means we’ll have to start thinking about cases like f.lux and Brackets.

Answering the last paragraph of that post and the first one of the next (they overlap on the idea I want to touch on), I’d say that’s fair enough, but I’d also argue that installers are a specific subset. They’re the minority, and I’m not sure the added complexity for new contributors will trump one window opening, specially if that ends up having to be the way most of the time anyway, due to app based uninstallers. Even regarding installers, some apps, like Little Snitch, have an app based one (in addition to the uninstaller), and that’s not just a wrapper for a pkg file, like SteerMouse.

My point being, maybe homebrew-cask will not be able to act exclusively from the terminal anyway, and if that’s the case, its design should be prepared to handle that limitation — whether that be accepting that it can never be fully automated (although I’d say I agree in full with that sentiment, I also believe it should be), implementing some kind of AppleScript hack to act on the uninstaller, rejecting the apps that break the cli-only flow, or other.

Can you point me to the examples of app based uninstallers?

If you look at the current pull requests, all of them that have uninstall use app based uninstallers (I did not check all of them, but the ones I did are commented). TeamViewer, GPGTools, GPGMail, GPGServices, MacGPG2 (yes, these last four are all from the same contributor), CrashPlan (has a shell script uninstaller, inside the app), TotalFinder (has an applescript based uninstaller, inside), Google Hangouts plugin (seems like the uninstaller was removed), Little Snitch (seemed to have no script alternatives), TotalTerminal.

Before this project, you could say the same thing about app downloads too. :)

Good point. However, this is still not a standard, necessarily. Homebrew-cask is more of a centralised resource, and in this case I’d define a standard as being an ideal (as opposed to a product) that is agreed on and implemented with the same set of rules across different projects.

My inclination is to try it this way for a bit and then see how bad it gets.

Again, I completely agree. Like I said, “naturally, in the end it all depends on the actual implementation and the way that different apps integrate different uninstallers, so it’s a concern for when it lands and there are some reactions”.

@phinze
Copy link
Contributor Author

phinze commented Jul 5, 2013

Yeah sorry about the edits - I managed to swipe the submit button accidentally after the 3rd line. 😊

Thanks for the well written response, @vitorgalvao - taking a look at a few of those pull requests, you're right that a .app-based uninstaller is a common pattern.

I've actually been doing some research that may help solve both of our concerns.

Turns out anything installed with a pkg-based installer drops a standard format receipt of all installed files, that can in turn be parsed by pkgutil. We could theoretically ignore app-specific uninstallers and just remove the OSX-approved list of installed files using standard tools.

http://superuser.com/questions/36567/how-do-i-uninstall-any-apple-pkg-package-file

Of course on a case-by-case basis we'll need to make sure that the above strategy doesn't miss anything.

I'll go ahead and start the pull request with the initial implementation so we can start to flush the list of casks on hold, and hopefully we can eventually land on something wonderful and awesome. ;)

@vitorgalvao
Copy link
Member

That is a great find. I’m all for that solution, if it turns out to be a viable one (and on first look, it looks like it just might be). It’s terminal-based, should work for every pkg based app out of the box (like you said, we’d have to check, just to be sure), could render the uninstall line unnecessary, and even work on apps that don’t come with an uninstaller. LittleSnitch is still a problem, but so far we have no good solution for that one, anyway.

Since you’ve been researching this for a while, even to make it work with Vagrant and Virtualbox, I’d say you’re the most qualified to weight the pros and cons of that approach, but so far, I really like what I see.

When the feature is merged, this one should probably go along for the ride.

phinze added a commit that referenced this issue Jul 22, 2013
accepts a single argument, which is a relative path to a pkg
inside the extracted Cask; homebrew-cask will attempt to install this
pkg after the Cask is extracted via `installer`

because of the many different ways uninstallers work, this
has several features:

 - `:script`: a script in the Cask which serves as an uninstaller (e.g.
   Vagrant, VirtualBox), uses `:args`, and `:input` keys to interact
   with said script
 - `:pkgutil`: a regexp which captures all package_ids installed by this
   cask; homebrew-cask will list all files installed under these ids and
   remove them
 - `:launchctl`: a list of bundle_ids for services that should be
   removed by homebrew-cask
 - `:files`: a fallback list of files to manually remove; helps when
   uninstallers miss something

refs #661
@phinze
Copy link
Contributor Author

phinze commented Jul 22, 2013

Oh hey this is merged now! 🍪

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants