diff --git a/.travis.yml b/.travis.yml index e2225fe759a4..b6d31039ed8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ before_script: # path-quoting is different here due to YAML constraints # @@@ todo: setting the --seed here is an ugly temporary hack, to remain only until test-suite glitches are fixed. script: - - /System/Library/Frameworks/Ruby.framework/Versions/"${CASK_RUBY_TEST_VERSION}"/usr/bin/bundle exec "/System/Library/Frameworks/Ruby.framework/Versions/${CASK_RUBY_TEST_VERSION}/usr/bin/rake" test TESTOPTS="--seed=14828" + - /System/Library/Frameworks/Ruby.framework/Versions/"${CASK_RUBY_TEST_VERSION}"/usr/bin/bundle exec "/System/Library/Frameworks/Ruby.framework/Versions/${CASK_RUBY_TEST_VERSION}/usr/bin/rake" test TESTOPTS="--seed=14829" notifications: irc: diff --git a/doc/CASK_LANGUAGE_REFERENCE.md b/doc/CASK_LANGUAGE_REFERENCE.md index e7107a766d40..42ba42e72d54 100644 --- a/doc/CASK_LANGUAGE_REFERENCE.md +++ b/doc/CASK_LANGUAGE_REFERENCE.md @@ -114,6 +114,7 @@ Each Cask must declare one or more *artifacts* (i.e. something to install) | `postflight` | yes | a Ruby block containing postflight install operations | `uninstall_preflight` | yes | a Ruby block containing preflight uninstall operations (needed only in very rare cases) | `uninstall_postflight` | yes | a Ruby block containing postflight uninstall operations +| `accessibility_access` | no | `true` if the application should be granted accessibility access | `container :nested =>` | no | relative path to an inner container that must be extracted before moving on with the installation; this allows us to support dmg inside tar, zip inside dmg, etc. | `container :type =>` | no | a symbol to override container-type autodetect. may be one of: `:air`, `:bz2`, `:cab`, `:dmg`, `:generic_unar`, `:gzip`, `:otf`, `:pkg`, `:rar`, `:seven_zip`, `:sit`, `:tar`, `:ttf`, `:xar`, `:zip`, `:naked`. (example [parse.rb](../Casks/parse.rb)) | `tags` | no | a list of key-value pairs for Cask annotation. Not free-form. (see also [Tags Stanza Details](#tags-stanza-details)) diff --git a/doc/cask_language_deltas.md b/doc/cask_language_deltas.md index a6b0da5bce40..9f060400c160 100644 --- a/doc/cask_language_deltas.md +++ b/doc/cask_language_deltas.md @@ -73,25 +73,26 @@ features which are available for the current Cask. ## Renamed Forms (1.0) -| old form | new form -| ------------------------------------- |---------------- -| `after_install` | [`postflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) -| `after_uninstall` | [`uninstall_postflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) -| `arch_only` (within `caveats`) | [`depends_on :arch`](CASK_LANGUAGE_REFERENCE.md#depends_on-arch) -| `before_install` | [`preflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) -| `before_uninstall` | [`uninstall_preflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) -| `container_type` | [`container :type`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) -| `depends_on_formula` | [`depends_on :formula`](CASK_LANGUAGE_REFERENCE.md#depends_on-formula) -| `destination_path` | [`staged_path`](CASK_LANGUAGE_REFERENCE.md#caveats-as-a-string) -| `install` | [`pkg`](CASK_LANGUAGE_REFERENCE.md#pkg-stanza-details) -| `link` | [`app`](CASK_LANGUAGE_REFERENCE.md#app-stanza-details) (or sometimes `suite` or `artifact`) -| `manual_installer` (within `caveats`) | [`installer :manual`](CASK_LANGUAGE_REFERENCE.md#installer-manual) -| `nested_container` | [`container :nested =>`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) -| `os_version_only` (within `caveats`) | [`depends_on :macos`](CASK_LANGUAGE_REFERENCE.md#depends_on-macos) -| `title` (in interpolations) | [`token`](CASK_LANGUAGE_REFERENCE.md#caveats-as-a-string) -| `uninstall :files` | [`uninstall :delete`](CASK_LANGUAGE_REFERENCE.md#uninstall-key-delete) -| `version 'latest'` | [`version :latest`](CASK_LANGUAGE_REFERENCE.md#required-stanzas) -| `x11_required` (within `caveats`) | [`depends_on :x11`](CASK_LANGUAGE_REFERENCE.md#all-depends_on-keys) +| old form | new form +| --------------------------------------- |---------------- +| `after_install` | [`postflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) +| `after_uninstall` | [`uninstall_postflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) +| `arch_only` (within `caveats`) | [`depends_on :arch`](CASK_LANGUAGE_REFERENCE.md#depends_on-arch) +| `assistive_devices` (within `caveats`) | [`accessibility_access`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) +| `before_install` | [`preflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) +| `before_uninstall` | [`uninstall_preflight`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) +| `container_type` | [`container :type`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) +| `depends_on_formula` | [`depends_on :formula`](CASK_LANGUAGE_REFERENCE.md#depends_on-formula) +| `destination_path` | [`staged_path`](CASK_LANGUAGE_REFERENCE.md#caveats-as-a-string) +| `install` | [`pkg`](CASK_LANGUAGE_REFERENCE.md#pkg-stanza-details) +| `link` | [`app`](CASK_LANGUAGE_REFERENCE.md#app-stanza-details) (or sometimes `suite` or `artifact`) +| `manual_installer` (within `caveats`) | [`installer :manual`](CASK_LANGUAGE_REFERENCE.md#installer-manual) +| `nested_container` | [`container :nested =>`](CASK_LANGUAGE_REFERENCE.md#optional-stanzas) +| `os_version_only` (within `caveats`) | [`depends_on :macos`](CASK_LANGUAGE_REFERENCE.md#depends_on-macos) +| `title` (in interpolations) | [`token`](CASK_LANGUAGE_REFERENCE.md#caveats-as-a-string) +| `uninstall :files` | [`uninstall :delete`](CASK_LANGUAGE_REFERENCE.md#uninstall-key-delete) +| `version 'latest'` | [`version :latest`](CASK_LANGUAGE_REFERENCE.md#required-stanzas) +| `x11_required` (within `caveats`) | [`depends_on :x11`](CASK_LANGUAGE_REFERENCE.md#all-depends_on-keys) ## All Supported Stanzas (1.0) @@ -143,7 +144,6 @@ For use in *eg* interpolation: ## Caveats Mini-DSL (1.0) - * [`assistive_devices`](CASK_LANGUAGE_REFERENCE.md#caveats-mini-dsl) * [`files_in_usr_local`](CASK_LANGUAGE_REFERENCE.md#caveats-mini-dsl) * [`logout`](CASK_LANGUAGE_REFERENCE.md#caveats-mini-dsl) * [`path_environment_variable(path)`](CASK_LANGUAGE_REFERENCE.md#caveats-mini-dsl) diff --git a/lib/cask/dsl.rb b/lib/cask/dsl.rb index 6d731b654060..bac08b268978 100644 --- a/lib/cask/dsl.rb +++ b/lib/cask/dsl.rb @@ -50,6 +50,8 @@ def artifacts; self.class.artifacts; end def caveats; self.class.caveats; end + def accessibility_access; self.class.accessibility_access; end + module ClassMethods # A quite fragile shim to allow "full_name" be exposed as simply "name" @@ -205,6 +207,13 @@ def caveats(*string, &block) end end + def accessibility_access(accessibility_access=nil) + if @accessibility_access and !accessibility_access.nil? + raise CaskInvalidError.new(self.token, "'accessibility_access' stanza may only appear once") + end + @accessibility_access ||= accessibility_access + end + def self.ordinary_artifact_types @@ordinary_artifact_types ||= [ :app, diff --git a/lib/cask/dsl/postflight.rb b/lib/cask/dsl/postflight.rb index 2c70008df888..aaa1a8eebb38 100644 --- a/lib/cask/dsl/postflight.rb +++ b/lib/cask/dsl/postflight.rb @@ -8,21 +8,6 @@ def suppress_move_to_applications(options = {}) @command.run!('/usr/bin/defaults', :args => ['write', bundle_identifier, key, '-bool', 'true']) end - def enable_accessibility_access - if MacOS.version >= :mavericks - @command.run!('/usr/bin/sqlite3', - :args => [ - Cask.tcc_db, - "INSERT INTO access VALUES('kTCCServiceAccessibility','#{bundle_identifier}',0,1,1,NULL);", - ], - :sudo => true) - else - @command.run!('/usr/bin/touch', - :args => [Cask.pre_mavericks_accessibility_dotfile], - :sudo => true) - end - end - def method_missing(method, *args) Cask::Utils.method_missing_message(method, @cask.to_s, 'postflight') return nil diff --git a/lib/cask/dsl/uninstall_preflight.rb b/lib/cask/dsl/uninstall_preflight.rb index 5599aac68527..e7acb0ac0632 100644 --- a/lib/cask/dsl/uninstall_preflight.rb +++ b/lib/cask/dsl/uninstall_preflight.rb @@ -3,22 +3,6 @@ class Cask::DSL::UninstallPreflight < Cask::DSL::Base include Cask::Staged - def disable_accessibility_access - if MacOS.version >= :mavericks - @command.run!('/usr/bin/sqlite3', - :args => [ - Cask.tcc_db, - "DELETE FROM access WHERE client='#{bundle_identifier}';", - ], - :sudo => true) - else - opoo <<-EOS.undent - Accessibility access was enabled for #{@cask}, but it is not safe to disable - automatically on this version of OS X. See System Preferences. - EOS - end - end - def method_missing(method, *args) Cask::Utils.method_missing_message(method, @cask.to_s, 'uninstall_preflight') return nil diff --git a/lib/cask/installer.rb b/lib/cask/installer.rb index 53f94cd6ac68..8a0de7ee1428 100644 --- a/lib/cask/installer.rb +++ b/lib/cask/installer.rb @@ -1,7 +1,15 @@ require 'rubygems' +require 'cask/staged' class Cask::Installer + # todo: it is unwise for Cask::Staged to be a module, when we are + # dealing with both staged and unstaged Casks here. This should + # either be a class which is only sometimes instantiated, or there + # should be explicit checks on whether staged state is valid in + # every method. + include Cask::Staged + PERSISTENT_METADATA_SUBDIRS = [ 'gpg' ] def initialize(cask, command=Cask::SystemCommand) @@ -51,6 +59,7 @@ def install(force=false) download extract_primary_container install_artifacts + enable_accessibility_access rescue StandardError => e purge_versioned_files raise e @@ -172,8 +181,45 @@ def print_caveats self.class.print_caveats(@cask) end + # todo: logically could be in a separate class + def enable_accessibility_access + return unless @cask.accessibility_access + ohai 'Enabling accessibility access' + if MacOS.version >= :mavericks + @command.run!('/usr/bin/sqlite3', + :args => [ + Cask.tcc_db, + "INSERT INTO access VALUES('kTCCServiceAccessibility','#{bundle_identifier}',0,1,1,NULL);", + ], + :sudo => true) + else + @command.run!('/usr/bin/touch', + :args => [Cask.pre_mavericks_accessibility_dotfile], + :sudo => true) + end + end + + def disable_accessibility_access + return unless @cask.accessibility_access + if MacOS.version >= :mavericks + ohai 'Disabling accessibility access' + @command.run!('/usr/bin/sqlite3', + :args => [ + Cask.tcc_db, + "DELETE FROM access WHERE client='#{bundle_identifier}';", + ], + :sudo => true) + else + opoo <<-EOS.undent + Accessibility access was enabled for #{@cask}, but it is not safe to disable + automatically on this version of OS X. See System Preferences. + EOS + end + end + def uninstall(force=false) odebug "Cask::Installer.uninstall" + disable_accessibility_access uninstall_artifacts purge_versioned_files purge_caskroom_path if force diff --git a/lib/cask/staged.rb b/lib/cask/staged.rb index cc9d38f08edf..6ee2c60dba8f 100644 --- a/lib/cask/staged.rb +++ b/lib/cask/staged.rb @@ -3,7 +3,7 @@ def info_plist(index = 0) index = 0 if index == :first index = 1 if index == :second index = -1 if index == :last - staged_path.join(@cask.artifacts[:app].to_a.at(index).first, 'Contents', 'Info.plist') + @cask.staged_path.join(@cask.artifacts[:app].to_a.at(index).first, 'Contents', 'Info.plist') end def plist_exec(cmd) @@ -15,6 +15,6 @@ def plist_set(key, value) end def bundle_identifier - plist_exec('Print CFBundleIdentifier') + plist_exec('Print CFBundleIdentifier').stdout.chomp end end diff --git a/lib/cask/utils.rb b/lib/cask/utils.rb index a044e5951abc..1801be27825d 100644 --- a/lib/cask/utils.rb +++ b/lib/cask/utils.rb @@ -99,6 +99,7 @@ def dumpcask :conflicts_with, :container, :gpg, + :accessibility_access, ].each do |method| printable_method = method.to_s printable_method = "name" if printable_method == "full_name" diff --git a/test/cask/accessibility_test.rb b/test/cask/accessibility_test.rb new file mode 100644 index 000000000000..491a20861eaf --- /dev/null +++ b/test/cask/accessibility_test.rb @@ -0,0 +1,61 @@ +require 'test_helper' + +# todo: this test should be named after the corresponding class, once +# that class is abstracted from installer.rb. +describe "Accessibility Access" do + before do + cask = Cask.load('with-accessibility-access') + @installer = Cask::Installer.new(cask, Cask::FakeSystemCommand) + end + + describe "install" do + it "can enable accessibility access" do + MacOS.stubs(:version => OS::Mac::Version.new('10.9')) + + @installer.stubs(:bundle_identifier => 'com.example.BasicCask') + + Cask::FakeSystemCommand.expects_command( + ['/usr/bin/sudo', '-E', '--', '/usr/bin/sqlite3', Cask.tcc_db, %q{INSERT INTO access VALUES('kTCCServiceAccessibility','com.example.BasicCask',0,1,1,NULL);}] + ) + shutup do + @installer.enable_accessibility_access + end + end + + it "can enable accessibility access in OS X versions prior to Mavericks" do + MacOS.stubs(:version => OS::Mac::Version.new('10.8')) + + Cask::FakeSystemCommand.expects_command( + ['/usr/bin/sudo', '-E', '--', '/usr/bin/touch', Cask.pre_mavericks_accessibility_dotfile] + ) + shutup do + @installer.enable_accessibility_access + end + end + end + + describe "uninstall" do + it "can disable accessibility access" do + MacOS.stubs(:version => OS::Mac::Version.new('10.9')) + + @installer.stubs(:bundle_identifier => 'com.example.BasicCask') + + Cask::FakeSystemCommand.expects_command( + ['/usr/bin/sudo', '-E', '--', '/usr/bin/sqlite3', Cask.tcc_db, %q{DELETE FROM access WHERE client='com.example.BasicCask';}] + ) + shutup do + @installer.disable_accessibility_access + end + end + it "warns about disabling accessibility access on old OS X versions" do + MacOS.stubs(:version => OS::Mac::Version.new('10.8')) + + @installer.stubs(:bundle_identifier => 'com.example.BasicCask') + + out, err = capture_io do + @installer.disable_accessibility_access + end + err.must_match('Warning: Accessibility access was enabled for with-accessibility-access, but it is not safe to disable') + end + end +end diff --git a/test/cask/dsl/postflight_test.rb b/test/cask/dsl/postflight_test.rb index eb43c3dead91..cd24bf1d20f3 100644 --- a/test/cask/dsl/postflight_test.rb +++ b/test/cask/dsl/postflight_test.rb @@ -18,21 +18,17 @@ end it "can execute commands on the Info.plist file" do + @dsl.stubs(:bundle_identifier => 'com.example.BasicCask') + Cask::FakeSystemCommand.expects_command( ['/usr/libexec/PlistBuddy', '-c', 'Print CFBundleIdentifier', @dsl.info_plist] ) @dsl.plist_exec('Print CFBundleIdentifier') end - it "can retrieve the bundle identifier for the primary app" do - Cask::FakeSystemCommand.stubs_command( - ['/usr/libexec/PlistBuddy', '-c', 'Print CFBundleIdentifier', @dsl.info_plist], - 'com.example.BasicCask' - ) - @dsl.bundle_identifier.stdout.must_equal 'com.example.BasicCask' - end - it "can set a key in the Info.plist file" do + @dsl.stubs(:bundle_identifier => 'com.example.BasicCask') + Cask::FakeSystemCommand.expects_command( ['/usr/libexec/PlistBuddy', '-c', 'Set :JVMOptions:JVMVersion 1.6+', @dsl.info_plist] ) @@ -56,24 +52,4 @@ ) @dsl.suppress_move_to_applications :key => 'suppressMoveToApplications' end - - it "can enable accessibility access" do - MacOS.stubs(:version => OS::Mac::Version.new('10.9')) - - @dsl.stubs(:bundle_identifier => 'com.example.BasicCask') - - Cask::FakeSystemCommand.expects_command( - ['/usr/bin/sudo', '-E', '--', '/usr/bin/sqlite3', Cask.tcc_db, %q{INSERT INTO access VALUES('kTCCServiceAccessibility','com.example.BasicCask',0,1,1,NULL);}] - ) - @dsl.enable_accessibility_access - end - - it "can enable accessibility access in OS X versions prior to Mavericks" do - MacOS.stubs(:version => OS::Mac::Version.new('10.8')) - - Cask::FakeSystemCommand.expects_command( - ['/usr/bin/sudo', '-E', '--', '/usr/bin/touch', Cask.pre_mavericks_accessibility_dotfile] - ) - @dsl.enable_accessibility_access - end end diff --git a/test/cask/dsl/uninstall_preflight_test.rb b/test/cask/dsl/uninstall_preflight_test.rb index 72647c59be3c..aaafb87d9dbc 100644 --- a/test/cask/dsl/uninstall_preflight_test.rb +++ b/test/cask/dsl/uninstall_preflight_test.rb @@ -5,26 +5,4 @@ cask = Cask.load('basic-cask') @dsl = Cask::DSL::UninstallPreflight.new(cask, Cask::FakeSystemCommand) end - - it "can disable accessibility access" do - MacOS.stubs(:version => OS::Mac::Version.new('10.9')) - - @dsl.stubs(:bundle_identifier => 'com.example.BasicCask') - - Cask::FakeSystemCommand.expects_command( - ['/usr/bin/sudo', '-E', '--', '/usr/bin/sqlite3', Cask.tcc_db, %q{DELETE FROM access WHERE client='com.example.BasicCask';}] - ) - @dsl.disable_accessibility_access - end - - it "warns about disabling accessibility access on old OS X versions" do - MacOS.stubs(:version => OS::Mac::Version.new('10.8')) - - @dsl.stubs(:bundle_identifier => 'com.example.BasicCask') - - out, err = capture_io do - @dsl.disable_accessibility_access - end - err.must_match('Warning: Accessibility access was enabled for basic-cask, but it is not safe to disable') - end end diff --git a/test/cask/staged_test.rb b/test/cask/staged_test.rb new file mode 100644 index 000000000000..5d17ec28cd4d --- /dev/null +++ b/test/cask/staged_test.rb @@ -0,0 +1,18 @@ +require 'test_helper' + +# todo: this test should be named after the corresponding class, once +# that class is abstracted from installer.rb. It makes little sense +# to be invoking bundle_identifier off of the installer instance. +describe "Operations on staged Casks" do + describe "bundle ID" do + it "fetches the bundle ID from a staged cask" do + transmission_cask = Cask.load('local-transmission') + tr_installer = Cask::Installer.new(transmission_cask) + + shutup do + tr_installer.install + end + tr_installer.bundle_identifier.must_equal('org.m0k.transmission') + end + end +end diff --git a/test/support/Casks/with-accessibility-access.rb b/test/support/Casks/with-accessibility-access.rb new file mode 100644 index 000000000000..b1075fdca3c5 --- /dev/null +++ b/test/support/Casks/with-accessibility-access.rb @@ -0,0 +1,11 @@ +cask :v1test => 'with-accessibility-access' do + version '1.2.3' + sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b' + + url 'http://example.com/TestCask.dmg' + homepage 'http://example.com/' + + app 'TestCask.app' + + accessibility_access true +end