Tomo::Remote

A Remote represents an SSH connection to a remote host and provides a façade for building and running shell scripts on that host. A Remote instance is always implicitly available within the context of a task implementation as remote. The tomo framework takes care of initializing the SSH connection and setting this reference.

The most basic use of Remote is the run method, which executes a script on the remote host:

remote.run "echo", "hello world"

For building more complex scripts, Remote offers a variety of builder methods: chdir, env, prepend, and umask. Here is an example of some of them:

remote.chdir(paths.current) do
  remote.prepend("bundle exec") do
    remote.env(DISABLE_SPRING: "1") do
      remote.run("rails", "db:prepare")
    end
  end
end
# $ cd /var/www/my-app/current && export DISABLE_SPRING=1 && bundle exec rails db:prepare

Instance methods

In addition the methods listed here, all helpers provided by the core plugin are also available, as are helpers from any other plugins that have been explicitly loaded in .tomo/config.rb. Refer to the documentation for each plugin for details.

run(*command, **options) → Tomo::Result

Runs a shell script on the remote host via SSH.

The command can take one of two forms. If given as a single string, the command is executed as a shell script directly; no escaping is performed. This is useful if your script needs to specify output redirection (> or >>), pipes, or other shell logic (&& or ||). For example:

remote.run "bundle exec rails db:prepare > /dev/null && echo 'All set!'"
# $ bundle exec rails db:prepare > /dev/null && echo 'All set!'

If the command is given as multiple string arguments, then each argument is individually shell-escaped and then assembled into a shell script. This is the preferred way to safely run scripts, especially if the script relies on settings or other user input. For example:

settings[:greeting] # => "<this> is safe & easy"
remote.run "echo", settings[:greeting]
# $ echo \<this\>\ is\ safe\ \&\ easy

When a script is run it is first echoed to the console and all of its output (stdout and stderr) is streamed to the console as well. If the script fails then an exception will be raised. If the script succeeds, a Result object will be returned.

This behavior can be customized by specifying options, which can include:

Name Purpose Default
:echo Similar to -x in bash, setting echo: true will cause the script to be printed to the logger before it is run. If false, the script will be run without being printed. If a string is provided, the string will be printed instead of the actual script. This can be useful for redacting or abbreviating sensitive or very long scripts. true
:silent Normally stdout and stderr of the remote script are printed to the logger. Setting silent: true will silence this output. Note that even if silenced, the output can still be accessed via the Result. false
:raise_on_error By default, if the remote script fails (i.e. returns an exit status other than 0), tomo will raise an exception, stopping execution. If the script being executed is expected to fail, or you would like to take action based on failure, set raise_on_error: false. In this case a failed script will return a Result with failure? set to true. true
:default_chdir The working directory where the script will be run by default, if chdir is not used. If not specified, the working directory is based on the SSH server’s default login directory (almost always this is the user’s home directory). nil
:attach Setting attach: true will cause the script to be run as if attach had been called instead. false
:pty Setting pty: true will instruct the SSH client to allocate a pseudo-tty when running the script. false

attach(*command, **options)

Runs a script on the remote host via SSH, just like run, except the Ruby process will be replaced with the SSH process and control completely handed over to SSH (this is done via Process.exec). In other words, tomo will immediately stop and it will be like you had run SSH directly.

This is useful for things like running a Rails console, where you would like to “attach” stdin/stdout to the remote process. Calling attach also implies pty: true.

remote.attach "bundle exec rails console"

attach accepts the same options as run (except for :attach, which is redundant).

chdir(dir, &block) → obj

Changes into the specified dir when executing a script via run or attach. Must be used with a block. This causes cd <dir> && to be prepended to the script.

remote.chdir "/opt/data" do
  remote.run "ls -al"
end
# $ cd /opt/data && ls -al

chdir returns the value of its block.

env(hash, &block) → obj

Sets environment variables when executing a script via run or attach. Must be used with a block. This causes export VAR1=value VAR2=value ... && to be prepended to the script. The environment variables are specified as a Ruby hash.

remote.env(CLICOLOR_FORCE: "1", RAILS_ENV: "production") do
  remote.run "bundle exec sidekiq"
end
# $ export CLICOLOR_FORCE=1 RAILS_ENV=production && bundle exec sidekiq

env returns the value of its block.

prepend(*command, &block) → obj

Prepends an arbitrary command when executing a script via run or attach. Must be used with a block.

remote.prepend "bundle", "exec" do
  remote.run "rails routes"
end
# $ bundle exec rails routes

prepend returns the value of its block.

umask(mask, &block) → obj

Sets a umask when executing a script via run or attach. Must be used with a block. This causes umask ... && to be prepended to the script. The mask can be an Integer (typically expressed in octal) or a String.

remote.umask 0o077 do
  remote.run "touch a_file"
end
# $ umask 0077 && touch a_file

umask returns the value of its block.

host → Tomo::Host

The remote SSH host that scripts will be run on.

release → Hash

A mutable Hash that can be used to share data about the release that is being deployed. Data stored in this Hash can be read by other tasks. In practice this is used by the git:create_release task to store the branch, author, SHA, date, etc. of the release. This data can then be accessed by other tasks that are interested in this information.

result = remote.run('git log -n1 --pretty=format:"%ae"')
remote.release[:author] = result.stdout.chomp
# remote.release[:author] can now be read by other tasks that connect to this host