Repository: capistrano/capistrano

Issue #1528: ArgumentError: wrong number of arguments (0 for 1) in dsl/env.rb
Created: 2015-11-25T17:24:07Z
I think #any? on https://github.com/capistrano/capistrano/blob/master/lib/capistrano/dsl/env.rb#L16 (link to same line in v3.4.0) should take an argument.
I have only encountered this bug in a project we recently switch over to JRuby, and it doesn't happen every time, so I'm not sure what triggers it. We have seen it happen for different tasks, e.g. deploy:cleanup, deploy:restart, so it doesn't seem isolated to a particular task. I don't think we have ever seen it with MRI.
I managed to get the error when running with --trace and here is that output. (I added puts "Parallel" on line 13 in sshkit/runners/parallel.rb for debugging purposes, that is also included below)
$ ruby -v
jruby 9.0.4.0 (2.2.2) 2015-11-12 b9fb7aa Java HotSpot(TM) 64-Bit Server VM 24.45-b08 on 1.7.0_45-b18 +jit [darwin-x86_64]

$ bundle exec cap production deploy:restart --trace
uri:classloader:/jruby/kernel/kernel.rb:17: warning: unsupported exec option: close_others
** Invoke production (first_time)
** Execute production
** Invoke load:defaults (first_time)
** Execute load:defaults
** Invoke chruby:validate (first_time)
** Execute chruby:validate
Parallel
Parallel
Parallel
** Invoke chruby:map_bins (first_time)
** Execute chruby:map_bins
** Invoke bundler:map_bins (first_time)
** Execute bundler:map_bins
** Invoke deploy:restart (first_time)
** Execute deploy:restart
** Invoke deploy:export_upstart (first_time)
** Invoke deploy:foreman:upload_procfile (first_time)
** Invoke deploy:foreman:generate_procfile (first_time)
** Execute deploy:foreman:generate_procfile
** Execute deploy:foreman:upload_procfile
Parallel
Parallel
Parallel
INFO Uploading tmp/Procfile 100.0%
INFO Uploading tmp/Procfile 100.0%
INFO Uploading tmp/Procfile 100.0%
** Execute deploy:export_upstart
Parallel
Parallel
Parallel
INFO [10502ae6] Running /usr/bin/env sudo /usr/local/bin/chruby-exec jruby-9 -- /home/deploy/twingly-lee/shared/bin/foreman export upstart /etc/init -a twingly-lee -u `whoami` -l /home/deploy/twingly-lee/shared/log as deploy@haldir.example.org
INFO [21abb117] Running /usr/bin/env sudo /usr/local/bin/chruby-exec jruby-9 -- /home/deploy/twingly-lee/shared/bin/foreman export upstart /etc/init -a twingly-lee -u `whoami` -l /home/deploy/twingly-lee/shared/log as deploy@huor.example.org
INFO [10502ae6] Finished in 10.656 seconds with exit status 0 (successful).
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@fili.example.org: wrong number of arguments (0 for 1)
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/runners/parallel.rb:17:in `block in execute'
ArgumentError: wrong number of arguments (0 for 1)
/Users/dentarg/.gem/jruby/2.2.2/gems/capistrano-3.4.0/lib/capistrano/dsl/env.rb:41:in `any?'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/command_map.rb:41:in `[]'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/command.rb:206:in `to_s'
org/jruby/RubyKernel.java:445:in `String'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/formatters/pretty.rb:23:in `write_command'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/formatters/pretty.rb:10:in `write'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/backends/netssh.rb:134:in `block in _execute'
org/jruby/RubyKernel.java:1755:in `tap'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/backends/netssh.rb:133:in `_execute'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/backends/netssh.rb:66:in `execute'
/Users/dentarg/.gem/jruby/2.2.2/gems/capistrano-3.4.0/lib/capistrano/dsl.rb:28:in `sudo'
/Users/dentarg/.gem/jruby/2.2.2/bundler/gems/capistrano-twingly-66461c19f8f5/lib/capistrano/twingly/tasks/upstart.rake:8:in `block in (root)'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/backends/abstract.rb:77:in `within'
/Users/dentarg/.gem/jruby/2.2.2/bundler/gems/capistrano-twingly-66461c19f8f5/lib/capistrano/twingly/tasks/upstart.rake:7:in `block in (root)'
org/jruby/RubyBasicObject.java:1670:in `instance_exec'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/backends/netssh.rb:54:in `run'
/Users/dentarg/.gem/jruby/2.2.2/gems/sshkit-1.7.1/lib/sshkit/runners/parallel.rb:14:in `block in execute'
Tasks: TOP => deploy:export_upstart

Duplicate issue #1568: Capistrano DSL breaking 3rd party library
Created: 2016-01-22T17:28:53Z
I've written a simple function to lookup the ip of a digitalocean server using its name using their official gem. This works in isolation, but not in capistrano.
The snippet looks like this:
require 'droplet_kit'

def digitalocean_ipv4_address_from_server_name(server_name)

    matched = []
    client = DropletKit::Client.new(access_token: 'redacted')
    client.droplets.all().each do |droplet|

        if droplet.name == server_name
            matched.push(droplet)
        end

    end

    if matched.length == 1
        return matched[0].networks.v4[0].ip_address
    end

    raise "Expected 1 server in digital ocean with name #{server_name}, found #{matched.length}."
end

puts digitalocean_ipv4_address_from_server_name('my-do-server-name');
And like this in capistrano staging.rb
require 'droplet_kit'

def digitalocean_ipv4_address_from_server_name(server_name)

    matched = []
    client = DropletKit::Client.new(access_token: 'redacted')
    client.droplets.all().each do |droplet|

        if droplet.name == server_name
            matched.push(droplet)
        end

    end

    if matched.length == 1
        return matched[0].networks.v4[0].ip_address
    end

    raise "Expected 1 server in digital ocean with name #{server_name}, found #{matched.length}."

end

server digitalocean_ipv4_address_from_server_name('my-do-server-name'),
  user: 'deploy',
  roles: %w{app}
This results in this error:
cap aborted!
ArgumentError: Action 'delete' is already defined on `DropletKit::DropletResource`
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/resource_kit-0.1.4/lib/resource_kit/method_factory.rb:7:in `block in construct'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/resource_kit-0.1.4/lib/resource_kit/method_factory.rb:4:in `construct'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/resource_kit-0.1.4/lib/resource_kit/resource.rb:20:in `resources'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/droplet_kit-1.3.3/lib/droplet_kit/resources/droplet_resource.rb:5:in `<class:DropletResource>'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/droplet_kit-1.3.3/lib/droplet_kit/resources/droplet_resource.rb:2:in `<module:DropletKit>'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/droplet_kit-1.3.3/lib/droplet_kit/resources/droplet_resource.rb:1:in `<top (required)>'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/droplet_kit-1.3.3/lib/droplet_kit/client.rb:22:in `resources'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/droplet_kit-1.3.3/lib/droplet_kit/client.rb:38:in `method_missing'
config/deploy/staging.rb:8:in `digitalocean_ipv4_address_from_server_name'
config/deploy/staging.rb:24:in `<top (required)>'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/capistrano-3.4.0/lib/capistrano/setup.rb:15:in `load'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/capistrano-3.4.0/lib/capistrano/setup.rb:15:in `block (2 levels) in <top (required)>'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/task.rb:240:in `call'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/task.rb:240:in `block in execute'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/task.rb:235:in `each'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/task.rb:235:in `execute'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/task.rb:179:in `block in invoke_with_call_chain'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/monitor.rb:211:in `mon_synchronize'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/task.rb:172:in `invoke_with_call_chain'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/task.rb:165:in `invoke'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:150:in `invoke_task'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:106:in `block (2 levels) in top_level'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:106:in `each'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:106:in `block in top_level'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:115:in `run_with_threads'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:100:in `top_level'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:78:in `block in run'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:176:in `standard_exception_handling'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rake/application.rb:75:in `run'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/capistrano-3.4.0/lib/capistrano/application.rb:15:in `run'
/Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/capistrano-3.4.0/bin/cap:3:in `<top (required)>'
/Users/aquaile/.rbenv/versions/2.2.3/bin/cap:23:in `load'
/Users/aquaile/.rbenv/versions/2.2.3/bin/cap:23:in `<main>'

After some digging, it looks to me like the capistrano DSL delete ^1 method is getting attached to any object. Debugging showed me it was this #<Method: XYZ(Capistrano::DSL::Env)#delete> defined here /Users/aquaile/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/capistrano-3.4.0/lib/capistrano/dsl/env.rb.
Am I misunderstanding/misusing something, or is this a bug?
Thanks,
PS. I'm not a ruby developer..

Closing comment (2016-01-29T21:57:44Z): I'm reopening this because I think something strange is going on here.
When I load things manually in irb, I get no errors:
require "capistrano/all"
require "droplet_kit"
client = DropletKit::Client.new(access_token: "redacted")
client.droplets.all().to_a
# => [...]
However when I put the same code in staging.rb:
require "droplet_kit"
client = DropletKit::Client.new(access_token: "redacted")
client.droplets.all().to_a
# => ArgumentError: Action 'delete' is already defined on `DropletKit::DropletResource`
This is suspiciously similar to #1528, where the Capistrano DSL is "leaking" into other objects.