Homebrew / homebrew-cask

🍻 A CLI workflow for the administration of macOS applications distributed as binaries

Home Page:https://brew.sh

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Roadmap: Rationalizing and Extending the Cask DSL (DSL 1.0)

rolandwalker opened this issue · comments

Background

Some of you may have noticed that #3066 works, but has been sitting unmerged
for a long time. The reason I pressed "pause" on that PR was the realization
that storing Cask files as metadata (while appealingly simple) would mean that
we need to keep backwards-compatibility with the DSL essentially forever.

We could still add stanzas or keys, but it would become difficult to remove
anything, because we would need to be able to load arbitrarily old metadata-Casks.

Recently, we did make substantial changes to the DSL (including the removal
of stanzas) all of which was accomplished pretty much glitch-free. (#2931, #4264,
#4257, #4689). The process goes something like this (duplicated in Tracker below):

  • push forward-compatible code, but do not document
  • cut a release
  • wait 6 weeks
  • document
    • explain new DSL forms
    • deprecate old DSL forms
  • amend Casks
  • wait a few more weeks
  • delete backward-compatible code
  • delete backward-looking documentation

Argument

Like most successful things, the Cask DSL has grown incrementally over time
and was not always planned. This seems like a good moment to review and
rationalize the DSL (before merging #3066 or something like it).

As with #4678, this is only a draft, and will be edited in-place.

Goals

  • keep things friendly toward first-time Cask authors
  • make things even more friendly toward first-time Cask authors
  • enabling tech for brew cask upgrade
  • remove everything we aren't committed to keeping
  • batch multiple DSL changes together, so we aren't always in a wait-6-weeks/amend-Casks loop

Proposed DSL Changes

  • Add app stanza, and substitute it for the most common case where link is currently used. (#4845)
    • affects 1304 Casks in main repo
    • consistency: most artifacts are named after the artifact type (a noun, not a verb)
      • note also that the internal classes have always used App for this
    • additional semantics can be attached to app: there is an Info.plist to consult for version numbers, the app can be shut down during upgrades, etc.
    • keep link in the DSL for unusual cases? No, this is a bad idea because it is a verb, and will be confusing when combined with an option to install by copying
    • This is the most inconvenient change, because some of us will continue to type link out of habit. However, the inconvenience is balanced by the fact that link will continue to work (it just won't gain new features as app will). No, that compatibility is not worth the potential confusion.
  • Add suite stanza for Casks that link a directory into ~/Applications (#4845)
    • affects 8 Casks in main repo
    • we may later append new keys to suite detailing the location of apps inside the folder
  • Add artifact stanza for "generic artifact", to cover corner cases (#6225)
  • Replace install with pkg (#4845)
    • affects 179 Casks in main repo
    • consistency: most artifacts are named after the artifact type (a noun, not a verb)
      • note also that the internal classes have always used Pkg for this
    • clarity: currently the semantics of install/uninstall are enforced
      such that uninstall is unavailable to non-pkg Casks. We need to
      break that linkage and make uninstall available for all Casks.
      Removing install as a keyword will help Cask authors intuit that
      install and uninstall are not connected (#4865).
  • Replace before_install/after_install with preflight/postflight (#4845)
    • affects 34 Casks in main repo
    • intention: to expose preflight and postflight as discrete verbs in the CLI
  • Replace before_uninstall/after_uninstall with uninstall_preflight/uninstall_postflight (#4845)
    • affects 5 Casks in main repo
    • consistency with preflight/postflight
  • Decide whether to remove caskroom_only stanza (#4866)
    • affects 13 Casks in main repo
    • caskroom_only currently does somewhat more than the name suggests, because the class is based on Cask::Artifact::Pkg rather than Cask::Artifact::Base. Specifically, :uninstall is fully respected.
    • If caskroom_only is preserved, then Cask::Artifact::CaskroomOnly should be based on Cask::Artifact::Base
    • if uninstall can be made available to all Cask types, then we might not need caskroom_only
      • caskroom_only is also used in a funny idiom with after_install, a new stanza might be needed for that (proposed installer :script below)
    • decision: keep, per discussion in #6243, but rename to stage_only
  • Add dsl stanza to store DSL specification version number? (#4870)
    • affects all Casks
    • cons: one more line of boilerplate/clutter
    • pros: great for future-proofing and stability
    • better way: instead of new method, include DSL version in revised header line (#5365)
  • Add license stanza (#4873, #6071, #6357)
    • affects all Casks
  • Replace depends_on_formula with extensible depends_on :formula (#4896)
    • affects 1 Cask in main repo, 11 Casks in fonts repo
    • original implementation was just a mistake
    • depends_on :formula matches Homebrew
  • Add conflicts_with stanza to complement depends_on (#5569)
  • Consider removing if statements in favor of a more meaningful syntax for variants
    • decided against. It's too many changes at once, can wait for DSL 2.x.
  • add copy stanza (name negotiable) to support special cases
    • I don't yet fully understand what it would do, but we can add the keyword now. As with appcast, it doesn't have to undertake a function until later.
    • decided against, will be covered by suite, see comments below.
  • add gpg stanza to support GPG signatures (#4848, #5975)
  • replace 'latest' string with :latest symbol in version stanza (#4849)
    • once upon a time, 'latest' was simply a convention, no different from other version strings at the technical level. Recently, we have been assigning special semantics to 'latest'. Changing it to a symbol underscores the special meaning at the language level. On the backend, we will enforce that a symbol argument for version must be one of a specific list (currently only :latest), which will catch errant spellings of the special value.
    • :latest will stringify to latest, so layout on disk will be unchanged.
    • the ability to use the string 'latest' would not be removed, though we might remove the special semantics from it over time
    • we will be attempting to reconcile string version values against version numbers from appcasts and bundle Info.plists. The symbol value :latest will also indicate "this is not a version-string: do not expect it to match appcast or Info.plist.
  • add zap, an extension of uninstall that removes more stuff (#4869, #6155, #6610, #6193)
    • the first implementation is extremely simple: an exact duplicate of the uninstall syntax, describing uninstall-like actions which do not run by default — only with the new zap command verb
    • I have never figured out how to best to dynamically read zap targets from the mackup project. We could simply mine mackup for a list of files as an initial seed.
  • extend appcast to include keys such as a checksum. (#4847)
    • The current single-value URL string is not sufficient to be useful without information about what content is expected at that URL
  • add installer :script (name negotiable) as a more direct way to express caskroom_only/after_install (#4951, #6306, #6660)
  • add :glob option to file targets which currently support :target. (#5043)
    • did not finish in time, bumped to DSL 1.1
    • possible alternate name :expand ?
    • possible alternate interface: wrap files with expand method: app expand('*/VLC.app')
    • currently, :target is interpreted inside Cask::Artifact::Symlinked. The relevant code should be moved to dsl.rb and/or constructors called from dsl.rb, after which it would be easier to add support for :expand
  • consider changing uninstall directive :files to :remove or :delete. (#4928)
    • language reason: to add :trash, an uninstall directive which works by moving files to the OS X Trash
    • functionality reason: moving Apps to Trash can allow the App to keep running during an upgrade, and then smoothly start again with the new version after relaunch.
  • add uninstall :rmdir (#6192)
  • add tags stanza (#4953)
    • not free-form. limited/defined set of keys:
      • :vendor
      • :name (removed in favor of toplevel stanza name)
      • the author would like font styles as well
      • other suggestions?
    • References: #2812
  • Remove limitations on Cask class names (#5365)
    • eg currently Casks are unable to have leading digits
    • @phinze had some work/thoughts along these lines, the key is "decouple cask names themselves from Ruby class names". On IRC one proposed format was
      phinze> likecask OnePassword; name '1password'; ... ; end
    • @rolandwalker's current proposal reads like cask :v1 => 'google-chrome' do; end
  • change container_type to container (#6068, #6118)
    • new syntax: container :type => :naked
    • rationale: extensible without adding a new stanza
  • merge nested_container into container (#6120)
    • new syntax: container :nested => <inner-container>
    • rationale
      • one less stanza
      • more mnemonic for first-time Cask author
  • promote manual_installer to a first-class artifact (#6660)
    • current position inside caveats is opaque
    • promotion needed to assess conflicts
    • proposed name: installer :manual
    • can merge with installer :script
  • add internet_plugin artifact per user request (#5923)
  • add postflight mini-DSL (#5723)
  • add accessibility_access (#7854)

Tracker

This tracker ignores new functionality such as implementing gpg on the backend or adding zap stanzas to Casks. The purpose is tracking the stabilization of Cask DSL 1.0 (purely as a language) and when we can merge #3066.

  • decide yes/no on all the above proposals (mostly done, caskroom_only to be revisited)
  • push PRs for all the above proposals (simultaneous with deciding)
  • merge forward-compatible code, but do not document (Goal: 1 Jul – done 28 Jul)
  • cut a release
  • wait 6 weeks (Goal: 8 Sep)
  • cut a new release to mark the start of transition (0.40.0)
  • update brew cask create template
    • app switchover (#6167)
    • add license stanza (stub value of :unknown) (#6426)
    • add tags stanza? (stub value of :vendor => :unknown?) No
    • ? add dsl stanza ? (value '1.0') not needed
  • document
    • add banner to README.md (#6072)
    • add new doc file for DSL delta (#6074, #6076)
    • update HACKING.md if needed (no change)
    • document altered semantics
      • uninstall now applies to all Casks (#6122)
      • link is now demoted to corner cases No, it will be removed.
        • completely remove from CONTRIBUTING.md (#6167)
        • update: link should be removed in favor of artifact (#6225)
    • update CONTRIBUTING.md for each change
      • app (#6167)
      • suite (#6210)
      • link (remove) (#6167)
      • pkg (#6121)
      • preflight, postflight, uninstall_preflight, uninstall_postflight (not present)
      • license (#6426)
      • depends_on :formula (not present)
      • depends_on :arch (not present)
      • depends_on :macos (not present)
      • depends_on :x11 (not present)
      • other depends_on stubs (not present)
      • conflicts_with (all stubs) (not present)
      • gpg (not present) (not present)
      • version :latest as symbol (not present)
      • zap (not present)
      • appcast (new keys) (not present)
      • installer :script (not present)
      • installer :manual (not present)
      • uninstall :delete (not present)
      • uninstall :trash (not present)
      • tags (not present)
      • container :type (not present)
      • container :nested (#6578)
      • uninstall :rmdir (not present)
      • artifact (not present)
      • header line / class name (delay until last; method_missing trick will not prompt users to upgrade) (#7311)
      • token (not present)
      • name (#7806)
      • plist_set postflight (not present)
      • suppress_move_to_applications postflight (not present)
      • accessibility_access (not present)
      • system_command postflight (not present)
    • update CASK_LANGUAGE_REFERENCE.md for each change (both addition and deprecation)
      • app (#6167)
      • suite (#6210)
      • link (remove) (#6167, #6230)
      • pkg (#6121)
      • preflight, postflight, uninstall_preflight, uninstall_postflight (#6074)
      • license (#6426)
      • depends_on :formula (#6138)
      • depends_on :arch (#7793)
      • depends_on :macos (#7696)
      • depends_on :x11 (#7845)
      • other depends_on stubs (#6138)
      • conflicts_with (all stubs) (#6736)
      • gpg (#6868)
      • version :latest as symbol (#6223, #6230)
      • zap (#6155, #6189)
      • appcast (new keys) (#6680)
      • installer :script (#6660)
      • installer :manual (#6660)
      • uninstall :delete (#6227)
      • uninstall :trash (#6507)
      • tags (#6509)
      • container :type (#6463)
      • container :nested (#6578)
      • uninstall :rmdir (#6192)
      • artifact (#6225)
      • header line / class name (delay until last; method_missing trick will not prompt users to upgrade) (#7311)
      • token (#7673)
      • name (#7806)
      • plist_set postflight (#7938)
      • suppress_move_to_applications postflight (#7938)
      • accessibility_access (#7854)
      • system_command postflight (bumped)
    • update cask_language_deltas.md
      • app (#6167)
      • suite (#6210)
      • link (remove) (#6167)
      • pkg (#6121, #6163)
      • preflight, postflight, uninstall_preflight, uninstall_postflight (#6074)
      • license (#6426)
      • depends_on :formula (#6138)
      • depends_on :arch (#7793)
      • depends_on :macos (#7696)
      • depends_on :x11 (#7845)
      • other depends_on stubs (#6138)
      • conflicts_with (all stubs) (#6736)
      • gpg (#6868)
      • version :latest as symbol (#6223, #6230)
      • zap (#6155)
      • appcast (new keys) (#6680)
      • installer :script (#6660)
      • installer :manual (#6660)
      • uninstall :delete (#6227)
      • uninstall :trash (#6507)
      • tags (#6509)
      • container :type (#6463)
      • container :nested (#6578)
      • uninstall :rmdir (#6192)
      • artifact (#6225)
      • header line / class name (delay until last; method_missing trick will not prompt users to upgrade) (#7311)
      • token (#7673)
      • name (#7806)
      • plist_set postflight (#7938)
      • suppress_move_to_applications postflight (#7938)
      • accessibility_access (#7854)
      • system_command postflight (bumped)
    • use new DSL forms in error messages
  • amend Casks
  • check/revise/stabilize methods used for interpolation
    • convert destination_path to staged_path (#6783 related: #6840)
    • convert title to token (#7673)
  • update test suite to use new forms
    • app (#6167)
    • suite (must add a test) (#7432)
    • artifact (must add a test) (#6225)
    • link (remove) (#7065)
      • backend refactor prerequisite
      • revise test names eg "linkables"
      • revise test descriptions eg "linkables"
      • revise test variables eg "linkables"
    • pkg (#6137)
    • preflight, postflight, uninstall_preflight, uninstall_postflight (#6115, #6461)
    • license (#6426)
    • depends_on :formula (#6138)
    • depends_on :arch (#7793)
    • depends_on :macos (#7696)
    • depends_on :x11 (#7845)
    • other depends_on stubs (bumped)
    • conflicts_with (all stubs) (bumped)
    • gpg (#4848, #5975)
    • version :latest as symbol (#6223)
    • zap (#6155)
    • appcast (new keys) (#6680)
    • installer :script (there is one test, but another would be good) (bumped)
    • installer :manual (bumped)
    • uninstall :delete (#6227)
    • uninstall :trash (bumped)
    • tags (#4953)
    • container :type (#6463, repaired in #7429)
    • container :nested (#6578)
    • uninstall :rmdir (#6192)
    • header line / class name (delay until last; method_missing trick will not prompt users to upgrade) (#7430)
    • header line / class name in rspec-driven tests (skippable for now as there are no file reads)
    • test invalid header line format (#7568)
    • test header line name-mismatch (#7568)
    • token (#7673)
    • name (#7805)
    • plist_set postflight (#5723)
    • suppress_move_to_applications postflight (#5723)
    • accessibility_access (#7854, previously #5723)
    • system_command postflight (#5723)
  • header line / class name touches addtl documentation & scripts. Changes needed for
  • final determination on whether stage_only can be removed
    • answer: technically it could be, but we will keep it per discussion in #6243, with caskroom_only being renamed to stage_only
    • remove it from Casks whenever possible (#6305)
    • promote manual_installer to a first-class artifact, installer :manual (#6660)
    • rename to stage_only (#7365)
    • update CASK_LANGUAGE_REFERENCE.md (#7365)
    • update cask_language_deltas.md (#7365)
    • enforce conflict between stage_only and any activatable artifact (#7365)
  • wait a few more weeks before deleting backward-compatible code (Goal: 29 Sep — unrealistic, probably closer to 1 Nov)
  • remove support for pre-1.0 DSL forms
    • remove title support (#7825)
    • remove assistive_devices caveats support (#7855)
    • remove os_version_only caveats support (#7794)
    • remove arch_only caveats support (#7810)
    • remove x11_required caveats support (#7848)
    • enable audit test against version 'latest' as string (#7504)
    • remove link support (#7065)
    • remove install support (#7268)
    • remove after_uninstall support (#7354)
    • remove after_install support (#7354)
    • remove before_uninstall support (#7354)
    • remove before_install support (#7354)
    • remove uninstall :files support (#7367)
    • remove depends_on_formula support (#7401)
    • remove container_type support (#7429)
    • remove nested_container support (#7451)
    • remove manual_installer caveat support (#7428)
    • remove destination_path support (#7506)
    • remove caskroom_only support (#7507)
    • remove support for old-form header line (#7450)
      • enforce that header line name must match filename (#7450)
    • search code for relevant todo comments
      • todo
      • temporary
      • transition
      • DSL
      • fixme
      • removeme
      • 0.50
      • 1.0
      • backward
      • forward
      • deprecated
  • remove support for 1.0 trial forms that were decided against
    • remove tags :name support (#7812)
  • recheck all devscripts, including sister repos (#7503)
  • enforce a valid + minimum DSL version in header line (#7530)
  • fill in transitional notes in cask_language_deltas.md (#7567)
  • delete backward-looking documentation from CASK_LANGUAGE_REFERENCE.md (#7531)
  • Merge #6117 (initial metadata support)
  • review caveats mini-DSL for removals
  • review postflight mini-DSL for inclusion
    • system_command is bumped to DSL 1.1
    • remove_accessibility_access should be renamed to disable_accessibility_access for consistency (#7684)
    • enable_accessibility_access / disable_accessibility_access should be combined into accessibility_access and moved out of postflight (#7854)
      is bumped to DSL 1.1 in favor of a new form outside of postflight
  • remove "incomplete" notice from cask_language_deltas.md (#7941)
  • mark any Casks using undocumented features as DSL 1.1 seems to be none
  • MERGE #3066
  • update HACKING.md if needed not needed
  • cut a new release to mark the end of transition (v0.50.0)
  • remove banner on README.md (goal: 1 Dec) (#7698)

References

This is a tracking issue referred to in #4678 , the roadmap for brew cask upgrade

There is a followup tracker at #7683.

We also need a stanza for after copying is implemented, to deal with special cases.

Will need to read through this tomorrow when I get a chance

@fanquake take your time, there is no hurry.

I guess I forgot to prioritize these. The important removal in my view is install. The important addition is app. The rest are under the category of "easier to make changes all at once than to parcel them out".

@vitorgalvao would that work as an option copy do ... end block? I.e. we add an extensible option keyword?

Also paging @phinze.

I don’t think we’d need a block. I was envisioning it simply as an optional stanza, since it won’t need extra options (this is really the only part with different needs in linking and copying), and should be needed sparingly. Something like (using gridwars as the example)

class Gridwars < Cask
  url 'http://gridwars.marune.de/bin/gridwars_osx_x86.zip'
  homepage 'http://gridwars.marune.de/'
  version '9.3.2006'
  sha256 '39694d6d7f7af1a25818d10dbd811d0309562622f91f43f71d8b217bccdb03fb'
  app 'gridwars_osx_x86/gridwars.app'
  copy 'gridwars_osx_x86'
end

copy is a verb, so we might want to change that.

@vitorgalvao I'm almost done with these proposals, except for your suggested copy, which I didn't entirely grasp.

  • Does copy always take a single argument?
  • Is it possible that the same need is covered by the suite stanza, coupled with a future --copy option? suite 'gridwars_osx_x86' would mean: "create ~/Applications/gridwars_osx_x86", whether by linking or copying.

Yes. As per your second point, suite seems like it will do it.

@caskroom/maintainers, all specific DSL proposals given above now have corresponding PRs.

Edit: as of 23 Jun 14, this includes new additions to the above.

@rolandwalker Just to clarify, how are you envisioning suite working? Looking through the documentation, it might be going in a slightly different direction than what I was thinking. Let’s take gridwars.rb as the example. Its zip contains a gridwars_osx_x86 directory, with gridwars.app and other assets (needed for the game to actually run) inside it. Since copying is to not be considered a stanza anymore (which I think is a good decision) here’s how I am envisioning suite:

  • app and suite are not mutually exclusive. An app can have either, neither (cases of pkg, or installer, for example), or both.
  • If only app or only suite are present, they’ll do the same: link or copy (when that’s implemented) their arguments to the applicable location.
  • If both app and suite are present, then they’ll work differently when linking or copying. When linking, only app will be considered1; when copying, only suite will be considered1.

So gridwars.rb, under this example, would become

cask :v1 => 'gridwars' do
  version '9.3.2006'
  sha256 '39694d6d7f7af1a25818d10dbd811d0309562622f91f43f71d8b217bccdb03fb'

  url 'http://gridwars.marune.de/bin/gridwars_osx_x86.zip'
  homepage 'http://gridwars.marune.de/'
  license :unknown

  app 'gridwars_osx_x86/gridwars.app'
  suite 'gridwars_osx_x86'
end

while apps like sketchup.rb (uses suite) and acorn.rb (uses app) would remain as they are, since copying or linking them should yield the same result.


1 It works since the link is still accessing the app in the directory with every other resource that might be needed.
2 It needs to be this way, since copying only the app would break the application.

@vitorgalvao, hmm, I think the implicit change in behavior described above would be unpredictable to both end-users and Cask authors.

However, there is a need describe the apps that are within a suite for other reasons, such as determining the runtime version number.

And I'm definitely in favor of giving control to end-users who prefer a flat Applications folder, or a hierarchical one. We just need to define a good interface for that. What about a command-line option and corresponding config-file option? I'm not saying this is a good choice of name, but

$ brew cask install --appfolder gridwars

@rolandwalker I’m not so much thinking about control, but breakage. In the gridwars example, linking only the app bundle (the current behaviour) makes a lot of sense because you don’t care about whatever else is in that directory, and having to navigate to it doesn’t make sense (you just want to execute the game, anyway). However, having the whole directory is a necessity when copying, because without it the game won’t run.

When I asked “how are you envisioning suite working”, it was the wrong question. What I should ask is “how are you envisioning copying working?” It’s fine if you’re just thinking “grab whatever is in app or suite and copy instead of linking”, but then why have suite at all? Then it’s just there for semantics, but that could’ve been solved by keeping link around and using it in app bundles or directories as needed. As it stands, I see no compelling reason for both app and suite to exist: they’re just doing the same.

Having a command-line option won’t work, because then we’re putting the burden on the user of knowing how the cask is structured. It’s safe to assume that once we have copying implemented, users will be divided between the ones that do one or the other exclusively. It doesn’t make sense for the copying user (that likely will have that option set in one of the shell’s startup files) to have to think “oh, in this articular cask I need to give this specific flag, or else it’ll break”. Even us wouldn’t be able to remember.

At last, glad to see this is through.