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

Dealing with apps that insist on non-symlink unportable installation? #6001

Closed
treyharris opened this issue Sep 3, 2014 · 9 comments
Closed

Comments

@treyharris
Copy link
Contributor

Sorry if this is a previously-discussed issue but I can't figure out the right search terms if so.

I submitted a pull request, #5902, a couple weeks ago with an example (the Voila screen grabber). I just decided to try one of Voila's competitors, Snagit, for which there's already a Cask. But when I ran brew cask install snagit, got a successful response, and tried to run the app, I found it suffers from the same problem I attempted to workaround with the rather kludgey cask for Voila I submitted.

These two apps require

  1. that they be installed into /Applications and nowhere else (not even ~/Applications).
  2. that they not be accessed by symlinks. Either an actual .app bundle or a Mac alias in /Applications is acceptable.

There's been no action on my pull request, so I'm guessing this is a difficult problem. If y'all have any input on the best way to solve it, I'm happy to submit a patch to solve it for both of these casks (and any others that might need the same treatment).

@rolandwalker
Copy link
Contributor

Hi!

I guess the issue with #5902 is the procedural complexity. You have definitely made it work, but this sort of logic belongs in the core. The Cask itself should be declarative.

Making other install methods work is not a tremendously thorny problem, and in concept is supported by both @phinze and myself, but it will take a little time, and nobody has worked on adding it to the core.

That's not entirely true: we have changed fonts to install via hardlinks in #2258. And @miguelfrde wrote code to use exotic HFS+ dir-hardlinks in #2312 (not merged yet, but not because of disinterest.) However, both of these approaches are limited because they hardcode a certain installation method to an artifact type: ie font and widget.

Actually, let's call the link step "activation" to differentiate from "install" as a whole.

What we need to do is to abstract out the "activation" phase in the backend, and allow any of the following equivalent actions to be substituted:

This also means abstracting inquiries about whether the App is currently activated, as well as uninstall actions, together in subclasses of Cask::Activator.

Then we need to add user interface so that a particular activation method can be specified within a Cask, or at the CLI, or — most importantly — in a persistent config file.

The main reason I personally have not worked on this functionality yet is that we don't yet have a config file. My thinking is that the most common use-case will be people who want to configure Cask to always make a full copy. You can see what I'm working on in #4688 in #4678. #4688 is nearly done now, and a config file is in the plan on #4678.

Of course, there is no objection to refactoring the activation-phase code now. If you or anyone else is interested in doing so, it would be entirely welcome.

@treyharris
Copy link
Contributor Author

The whole kit-and-kaboodle is a bit of an undertaking, but how about a (maybe) simpler and stupider solution? Since "alias records" (what Apple calls them) seem to work in several of these cases, semantically it would seem straightforward to create an ..alias..¹ analogue to the link directive; leave the canonical location of the app in .../Caskroom but put an alias for it in /Applications.

The aggravating part here is that I don't know a clean way to create the alias record from Ruby. There's no blessed way through Cocoa to create an alias record directly; one is instead supposed to ask Finder to do it.

Is it acceptable to have a new artifact stanza:

  finder_alias 'Voila.app', :target => '/Applications/Voila.app'

with the important bit being:

  # There's no way to atomically create and name an alias, so we
  # create it in a tmpdir and move it over.
  Dir.mktmpdir { |tmpdir_name|
    tmpdir = Pathname.new(tmpdir_name)
    @command.run!('/usr/bin/osascript',
      :args => ['-e',
        %Q{tell application "Finder" to } +
        %Q{make alias file to POSIX file "#{source_path}" } +
        %Q{at POSIX file "#{tmpdir}"}])
    @command.run!('/usr/bin/ditto', :args => [
        '--', tmpdir.join(source_path.basename), target_dir.join(target_path.basename)])
  }

? If so, I'm happy to put together an artifact/aliased.rb that provides this.

¹ Naming is a bit vexing here. alias would be the most obvious analogue, but it's already in use by Ruby. alias_record is the canonical name, but doesn't read well as a directive since both alias and link are both nouns and verbs, but record is not (in the sense of "alias record", at least; you can record something, but you can't alias record something.). finder_alias or mac_alias are both descriptive and seem okay read as nouns or as directives.

@rolandwalker
Copy link
Contributor

Yes, the whole kaboodle doesn't have to be done at once, and won't be — I was just trying to sketch context.

As interface, the finder_alias stanza is a non-starter, though. Even the link stanza is going away in a few days, in favor of app. The artifact stanzas will be uniformly descriptive nouns, not actions.

However, we do need an interface to set the preferred activation method within a Cask. That will probably look something like

  app 'Voila.app', :activation => :alias,
                   :target => '/Applications/Voila.app'

And yes, that can be done in isolation: config file, and all other interface elements, can be skipped.

Though I think when you get to the backend, you will find that since the existing code conflates the artifact type with the activation action, the easiest resolution is still going to be spinning artifact/symlinked.rb into activator/base.rb and activator/symlinked.rb. After which, activator/alias.rb would be something as straightforward as the current artifact/hardlinked.rb.

A complication, which doesn't have to be solved now: read_script_arguments in Cask::Artifact::Base (which parses :target) is grotty, and really belongs somewhere else, under Cask::DSL::.

As you are seriously interested, and have done the groundwork, I am seriously interested in assisting you. Normally here I'd be offering to meet you halfway by doing some of the refactor/tests/docs. However, we have our long-planned DSL transition on 8 Sep, and not everything is in place yet. That transition is going to absorb time-slices across the whole team, I'd guess for 3 weeks.

Another option is to leave this bug open and try to ping each other in a few weeks. This is desired functionality, and has been for some time. You are offering to help, and help is wanted.

PS I use frequently use mkalias from brew install osxutils.

@treyharris
Copy link
Contributor Author

I cloned your fork and poked around your branches, but your branching is complex and I don't actually see this stuff—is it pushed up so I could take a swag?

I'm going to get started an an artifact/aliased.rb unless you scream "no, it won't be merged!"

I'll see what I can do about refactoring; a large part of its code is similar to artifact/symlinked.rb, so there should be some refactoring there, at least.

@treyharris
Copy link
Contributor Author

Whoops! Sorry, @rolandwalker —our responses crossed. Okay, I'll hear that as your screaming "no, it won't be merged!" :-) I'm not sure how I can contribute, though; is the code you're talking about pushed anywhere?

@rolandwalker
Copy link
Contributor

Not screaming though. An artifact/aliased.rb prototype would contain all the knowledge you have worked out about osascript already, and allow you to test/refine. We could move those methods around later.

The only block is that finder_alias can't become a documented public-use stanza. By the way, we have internal/unstable/dev command verbs now such as brew cask _dump which start with underscore. See lib/cask/cli/internal*.rb. Similarly you could implement _finder_alias and we can even merge the code, just not doc it or use it for public Casks.

As for those files lib/cask/activator.rb, lib/cask/activator/base.rb, lib/cask/activator/symlinked.rb — they only exist in my head. That's the part of what I would like to do for you to make this contribution easier.

@treyharris
Copy link
Contributor Author

Cool, makes sense, I'll get started then with something that would work as a private directive in the current published state of homebrew-cask.

I just asked a question at #6025 that becomes a bit more urgent for me to solve for me to do that, though: how do I play around with this without wrecking my branches and/or my running system configuration? Unfortunately I only have the one Mac laptop I use for everything.

@rolandwalker
Copy link
Contributor

DSL 1.0 starts today. I added the app <name>, :activation => :alias syntax to the DSL 1.1 roadmap at #5586, and a reminder to work out a schedule.

@vitorgalvao
Copy link
Member

Linking apps is not a great behaviour and issues like this prove how broken it makes everything as we need workarounds on top of workarounds. It’s getting ridiculous, and the behaviour needs to change, which is in the plans.

@Homebrew Homebrew locked and limited conversation to collaborators May 8, 2018
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

3 participants