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

DSL: add installer stanza #6660

Merged
merged 5 commits into from
Oct 16, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 54 additions & 16 deletions doc/CASK_LANGUAGE_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Cask Domain-Specific Language (DSL) which are not needed in most cases.
* [App Stanza Details](#app-stanza-details)
* [Suite Stanza Details](#suite-stanza-details)
* [Pkg Stanza Details](#pkg-stanza-details)
* [Installer Stanza Details](#installer-stanza-details)
* [Depends_on Stanza Details](#depends_on-stanza-details)
* [Uninstall Stanza Details](#uninstall-stanza-details)
* [Zap Stanza Details](#zap-stanza-details)
Expand Down Expand Up @@ -95,6 +96,7 @@ Each Cask must declare one or more *artifacts* (i.e. something to install)
| `widget` | yes | relative path to a widget that should be linked into the `~/Library/Widgets` folder on installation (ALPHA: DOES NOT WORK YET)
| `suite` | yes | relative path to a containing directory that should be linked into the `~/Applications` folder on installation (see also [Suite Stanza Details](#suite-stanza-details))
| `artifact` | yes | relative path to an arbitrary path that should be symlinked on installation. This is only for unusual cases. The `app` stanza is strongly preferred when linking `.app` bundles.
| `installer` | yes | describes an executable which must be run to complete the installation. (see [Installer Stanza Details](#installer-stanza-details))

## Optional Stanzas

Expand All @@ -117,27 +119,28 @@ Each Cask must declare one or more *artifacts* (i.e. something to install)

The following stanzas are no longer in use.

| name | multiple occurrences allowed? | meaning |
| -------------------- |------------------------------ | ----------- |
| `after_install` | yes | an obsolete alternative to `postflight`
| `after_uninstall` | yes | an obsolete alternative to `uninstall_postflight`
| `before_install` | yes | an obsolete alternative to `preflight`
| `before_uninstall` | yes | an obsolete alternative to `uninstall_preflight`
| `container_type` | yes | an obsolete alternative to `container :type =>`
| `depends_on_formula` | yes | an obsolete alternative to `depends_on :formula =>`
| `install` | yes | an obsolete alternative to `pkg`
| `link` | yes | an obsolete alternative to `artifact`
| `no_checksum` | no | an obsolete alternative to `sha256 :no_check`
| name | meaning |
| ------------------------- | ----------- |
| `after_install` | an obsolete alternative to `postflight`
| `after_uninstall` | an obsolete alternative to `uninstall_postflight`
| `before_install` | an obsolete alternative to `preflight`
| `before_uninstall` | an obsolete alternative to `uninstall_preflight`
| `container_type` | an obsolete alternative to `container :type =>`
| `depends_on_formula` | an obsolete alternative to `depends_on :formula =>`
| `install` | an obsolete alternative to `pkg`
| `link` | an obsolete alternative to `artifact`
| `no_checksum` | an obsolete alternative to `sha256 :no_check`


## Legacy Forms

The following forms are no longer in use.

| name | meaning |
| -------------------- | ----------- |
| `uninstall :files` | an obsolete alternative to `uninstall :delete`
| `version 'latest'` | an obsolete alternative to `version :latest`
| name | meaning |
| ------------------------- | ----------- |
| `uninstall :files` | an obsolete alternative to `uninstall :delete`
| `version 'latest'` | an obsolete alternative to `version :latest`
| `manual_installer(path)` | when occurring within `caveats`, an obsolete alternative to `installer :script`


## Conditional Statements
Expand Down Expand Up @@ -223,7 +226,6 @@ The following methods may be called to generate standard warning messages:

| method | description |
| --------------------------------- | ----------- |
| `manual_installer(path)` | The user should execute an installer to complete the installation. `path` may be absolute, or relative to the Cask.
| `path_environment_variable(path)` | The user should make sure `path` is in their `$PATH` environment variable
| `zsh_path_helper(path)` | Zsh users must take additional steps to make sure `path` is in their `$PATH` environment variable
| `logout` | The user should log out and log back in to complete installation
Expand Down Expand Up @@ -463,6 +465,42 @@ Example:
pkg 'Soundflower.pkg', :allow_untrusted => true
```


## Installer Stanza Details

The `installer` stanza takes a series of key-value pairs, the first key of
which must be `:manual` or `:script`.

### Installer :manual

`installer :manual` takes a single string value, describing a GUI installer
which must be run by the user at a later time. The path may be absolute,
or relative to the Cask. Example:

```ruby
installer :manual => 'Little Snitch Installer.app'
```
### Installer :script

`installer :script` introduces a series of key-value pairs describing
a command which will automate completion of the install. The form is
similar to `uninstall :script`:

| key | value
| ----------------|------------------------------
| `:script` | path to an install script to be run via `sudo`. (Required first key.)
| `:args` | array of arguments to the install script
| `:input` | array of lines of input to be sent to `stdin` of the script
| `:must_succeed` | set to `false` if the script is allowed to fail
| `:sudo` | set to `false` if the script does not need `sudo`

The path may be absolute, or relative to the Cask. Example:

```ruby
installer :script => 'Adobe AIR Installer.app/Contents/MacOS/Adobe AIR Installer',
:args => %w[-silent]
```

## Depends_on Stanza Details

`depends_on` is used to declare dependencies required to install a Cask
Expand Down
28 changes: 15 additions & 13 deletions doc/cask_language_deltas.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ This notice will be removed for the final form.**
* *stub* - not yet functional
* `depends_on :java`
* *stub* - not yet functional
* `installer :script`
* `license`
* `suite`
* `tags`
Expand All @@ -53,19 +54,20 @@ This notice will be removed for the final form.**

## Renames (1.0)

| old form | new form
| --------------------- |----------------
| `after_install` | `postflight`
| `after_uninstall` | `uninstall_postflight`
| `before_install` | `preflight`
| `before_uninstall` | `uninstall_preflight`
| `container_type` | `container :type`
| `depends_on_formula` | `depends_on :formula`
| `install` | `pkg`
| `link` | `app` (or sometimes `suite` or `artifact`)
| `nested_container` | `container :nested =>`
| `uninstall :files` | `uninstall :delete`
| `version 'latest'` | `version :latest`
| old form | new form
| ------------------------------------------- |----------------
| `after_install` | `postflight`
| `after_uninstall` | `uninstall_postflight`
| `before_install` | `preflight`
| `before_uninstall` | `uninstall_preflight`
| `container_type` | `container :type`
| `depends_on_formula` | `depends_on :formula`
| `install` | `pkg`
| `link` | `app` (or sometimes `suite` or `artifact`)
| `nested_container` | `container :nested =>`
| `uninstall :files` | `uninstall :delete`
| `version 'latest'` | `version :latest`
| `manual_installer(path)` (within `caveats`) | `installer :manual`


## All Supported Stanzas (1.0)
Expand Down
1 change: 1 addition & 0 deletions lib/cask.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Cask; end
require 'cask/qualified_cask_name'
require 'cask/scopes'
require 'cask/source'
require 'cask/staged'
require 'cask/system_command'
require 'cask/underscore_supporting_uri'
require 'cask/url'
Expand Down
4 changes: 2 additions & 2 deletions lib/cask/artifact.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module Cask::Artifact; end
require 'cask/artifact/before_block'
require 'cask/artifact/colorpicker'
require 'cask/artifact/font'
require 'cask/artifact/install_script'
require 'cask/artifact/installer'
require 'cask/artifact/nested_container'
require 'cask/artifact/pkg'
require 'cask/artifact/prefpane'
Expand All @@ -35,7 +35,7 @@ def self.artifacts
[
Cask::Artifact::BeforeBlock,
Cask::Artifact::NestedContainer,
Cask::Artifact::InstallScript,
Cask::Artifact::Installer,
Cask::Artifact::App,
Cask::Artifact::Artifact, # generic 'artifact' stanza
Cask::Artifact::Colorpicker,
Expand Down
30 changes: 0 additions & 30 deletions lib/cask/artifact/install_script.rb

This file was deleted.

40 changes: 40 additions & 0 deletions lib/cask/artifact/installer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class Cask::Artifact::Installer < Cask::Artifact::Base

# todo: for backward compatibility, removeme
def install
install_phase
end

# todo: for backward compatibility, removeme
def uninstall
uninstall_phase
end

def install_phase
@cask.artifacts[self.class.artifact_dsl_key].each do |artifact|
if artifact.manual then
puts <<-EOS.undent
To complete the installation of Cask #{@cask}, you must also
run the installer at

'#{@cask.destination_path.join(artifact.manual)}'

EOS
else
executable, script_arguments = self.class.read_script_arguments(
artifact.script,
"#{self.class.artifact_dsl_key}",
{:must_succeed => true, :sudo => true},
{:print_stdout => true}
)
ohai "Running #{self.class.artifact_dsl_key} script #{executable}"
raise CaskInvalidError.new(@cask, "#{self.class.artifact_dsl_key} missing executable") if executable.nil?
@command.run(@cask.destination_path.join(executable), script_arguments)
end
end
end

def uninstall_phase
odebug "Nothing to do. The #{self.class.artifact_dsl_key} artifact has no uninstall phase."
end
end
2 changes: 2 additions & 0 deletions lib/cask/caveats.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def destination_path
# ( The return value of the last method in the block is also sent
# to the output by the caller, but that feature is only for the
# convenience of Cask authors. )

# todo: remove this method after DSL 1.0 transition
def manual_installer(path)
puts <<-EOS.undent
To complete the installation of Cask #{@cask}, you must also
Expand Down
19 changes: 8 additions & 11 deletions lib/cask/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
module Cask::DSL; end

require 'cask/dsl/base'
require 'cask/dsl/installed'
require 'cask/dsl/installer'
require 'cask/dsl/after_install'
require 'cask/dsl/after_uninstall'
require 'cask/dsl/before_install'
Expand Down Expand Up @@ -282,18 +282,15 @@ def self.ordinary_artifact_types
end
end

def install_script(*args)
unless args.length > 0
raise CaskInvalidError.new(self.title, "'install_script' stanza requires an argument")
def installer(*args)
if args.empty?
return artifacts[:installer]
end
executable = args.shift if args[0].kind_of? String
if args.length > 0
args = Hash.new().merge(*args)
else
args = Hash.new()
begin
artifacts[:installer] << Cask::DSL::Installer.new(*args)
rescue StandardError => e
raise CaskInvalidError.new(self.title, e)
end
args.merge!({ :executable => executable }) if executable
artifacts[:install_script] << args
end

attr_reader :sums
Expand Down
4 changes: 3 additions & 1 deletion lib/cask/dsl/after_install.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require 'cask/staged'

class Cask::DSL::AfterInstall < Cask::DSL::Base
include Cask::DSL::Installed
include Cask::Staged

def suppress_move_to_applications
system_command("/usr/bin/defaults", :args => ["write", bundle_identifier, "moveToApplicationsFolderAlertSuppress", "-bool", "true"])
Expand Down
4 changes: 3 additions & 1 deletion lib/cask/dsl/before_uninstall.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require 'cask/staged'

class Cask::DSL::BeforeUninstall < Cask::DSL::Base
include Cask::DSL::Installed
include Cask::Staged

def remove_accessibility_access
if MacOS.version >= :mavericks
Expand Down
39 changes: 39 additions & 0 deletions lib/cask/dsl/installer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class Cask::DSL::Installer

VALID_KEYS = Set.new [
:manual,
:script,
]

attr_accessor *VALID_KEYS

def initialize(*parameters)
unless parameters.length > 0
raise CaskInvalidError.new(self.title, "'installer' stanza requires an argument")
end
parameters = Hash.new().merge(*parameters)
if parameters.key?(:script) and ! parameters[:script].respond_to?(:key?)
if parameters.key?(:executable)
raise CaskInvalidError.new(self.title, "'installer' stanza gave arguments for both :script and :executable")
end
parameters[:executable] = parameters[:script]
parameters.delete(:script)
parameters = { :script => parameters }
end
unless parameters.keys.length == 1
raise "invalid 'installer' stanza: only one of #{VALID_KEYS.inspect} is permitted"
end
key = parameters.keys.first
raise "invalid 'installer' stanza key: '#{key.inspect}'" unless VALID_KEYS.include?(key)
writer_method = "#{key}=".to_sym
send(writer_method, parameters[key])
end

def to_yaml
@pairs.to_yaml
end

def to_s
@pairs.inspect
end
end
3 changes: 2 additions & 1 deletion lib/cask/dsl/installed.rb → lib/cask/staged.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module Cask::DSL::Installed
module Cask::Staged
def info_plist
"#{destination_path}/#{@cask.artifacts[:link].first.first}/Contents/Info.plist"
end

def plist_exec(cmd)
# todo: don't use external interface system_command
system_command("/usr/libexec/PlistBuddy", :args => ["-c", cmd, info_plist])
end

Expand Down
6 changes: 3 additions & 3 deletions test/cask/cli/info_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@
Cask title: with-caveats

Custom text via puts followed by DSL-generated text:
To complete the installation of Cask with-caveats, you must also
run the installer at
To use with-caveats, you may need to add the /custom/path/bin directory
to your PATH environment variable, eg (for bash shell):

'#{Cask.caskroom}/with-caveats/1.2.3/Installer.app'
export PATH=/custom/path/bin:"$PATH"

CLIOUTPUT
end
Expand Down
Loading