From 85adbbf7a13381a2ee8b66d8ac8487cffd6ee60d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Aug 2024 04:01:27 +0000 Subject: [PATCH] Deployed 5f6764b with mkdocs 1.6.0 [ci skip] --- index.html | 2 +- search/search_index.json | 2 +- sitemap.xml.gz | Bin 127 -> 127 bytes .../deploying-rails-from-scratch/index.html | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 9c97b8aa..ff212a54 100644 --- a/index.html +++ b/index.html @@ -456,5 +456,5 @@

Contribution guide

diff --git a/search/search_index.json b/search/search_index.json index f9416559..ae0b92e8 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Tomo Tomo is a friendly command-line tool for deploying Rails apps. \ud83d\udcbb Rich command-line interface with built-in bash completions \u2601\ufe0f Multi-environment and role-based multi-host support \ud83d\udc8e Everything you need to deploy a basic Rails app out of the box \ud83d\udd0c Easily extensible for polyglot projects (not just Rails!) \ud83d\udcda Quality documentation \ud83d\udd2c Minimal dependencies \u2192 See how tomo compares to other Ruby deployment tools like Capistrano and Mina. Quick start Usage Extending tomo Tutorials Blog posts Reference documentation FAQ Support License Code of conduct Contribution guide Quick start Installation Tomo is distributed as a ruby gem. To install: $ gem install tomo \ud83d\udca1 Protip: run tomo completion-script for instructions on setting up bash completions. Configuring a project Tomo is configured via a .tomo/config.rb file in your project. To get started, run tomo init to generate a configuration that works for a basic Rails app. An abbreviated version looks like this: # .tomo/config.rb plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"user@hostname.or.ip.address\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end Next steps \u2192 The reference docs have a complete guide to tomo configuration. \u2192 Check out the Deploying Rails From Scratch tutorial for a step-by-step guide to using tomo with a real app. Usage Once your project is configured, you can: Run tomo setup to prepare the remote host for its first deploy. Run tomo deploy to deploy your app. Use tomo run to invoke one-off tasks, like launching a Rails console. \ud83d\udca1 Protip: add -h or --help when running any of these commands to see detailed docs and examples. tomo setup tomo setup prepares the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. Out of the box, tomo will: Configure necessary environment variables, like RAILS_ENV and SECRET_KEY_BASE Install Ruby, Bundler, Node, Yarn, and dependencies Create all necessary deployment directories Create the Rails database, load the schema, and insert seed data \u2192 Here is the default list of tasks invoked by the setup command. \u2192 The tomo setup section of the reference docs explains supported command-line options. tomo deploy Whereas tomo setup is typically run once, you can use tomo deploy every time you want to deploy a new version of your app. The deploy command will sequentially run the deploy list of tasks specified in .tomo/config.rb . You can customize this list to meet the needs of your app. By default, tomo runs these tasks: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) \ud83d\udca1 Protip: you can abbreviate tomo commands, like tomo d for tomo deploy or tomo s for tomo setup . \u2192 Here is the default list of tasks invoked by the deploy command. \u2192 The tomo deploy section of the reference docs explains supported command-line options, like --dry-run . tomo run [TASK] Tomo can also run individual remote tasks on demand. You can use the tasks command to see the list of tasks tomo knows about. One of the built-in Rails tasks is rails:console , which brings up a fully-interactive Rails console over SSH. \ud83d\udca1 Protip: you can shorten this as tomo rails:console (the run command is implied). \u2192 The tomo run section of the reference docs explains supported command-line options and has more examples. Extending tomo Tomo has a powerful plugin system that lets you extend tomo by installing Ruby gems (e.g. tomo-plugin-sidekiq ). You can also define plugins on the fly within your project by adding simple .rb files to .tomo/plugins/ . These plugins can define tasks as plain ruby methods. For example: # .tomo/plugins/my-plugin.rb def hello remote.run \"echo\", \"hello\", settings[:application] end Load your plugin in config.rb like this: # .tomo/config.rb plugin \"./plugins/my-plugin.rb\" And run it! \u2192 The Writing Custom Tasks tutorial has an in-depth explanation of how plugins work. \u2192 The TaskLibrary API is tomo\u2019s DSL for building tasks. \u2192 The Publishing a Plugin tutorial explains how to package your plugin as a Ruby gem to share it with the community. Tutorials Deploying Rails From Scratch Writing Custom Tasks Publishing a Plugin Blog posts Automate Rails deployments with GitHub Actions Reference documentation Configuration Commands init setup deploy run tasks Plugins core bundler env git nodenv puma rails rbenv API Host Logger Paths PluginDSL Remote Result TaskLibrary Testing::MockPluginTester FAQ What does the unsupported option \"accept-new\" error mean? By default, tomo uses the \u201caccept-new\u201d value for the StrictHostKeyChecking option, which is supported by OpenSSH 7.6 and newer. If you are using an older version, this will cause an error. As a workaround, you can override tomo\u2019s default behavior like this: # Replace \"accept-new\" with something compatible with older versions of SSH set ssh_strict_host_key_checking: true # or false Can I deploy multiple apps to a single host? Tomo relies on the host user\u2019s bash profile for various things, like setting environment variables and initializing rbenv and nodenv. This makes it impractical to deploy multiple apps to a single host using the same deploy user. The solution is to create multiple users on the remote host, and then configure a different user for deploying each app. That way each user can have its own distinct environment variables and you can easily configure each app differently without risking conflicts. Refer to the tomo Rails tutorial for instructions on creating a deploy user. E.g. app1 would be configured to deploy as: host \"app1@example.com\" And app2 would be configured to deploy as: host \"app2@example.com\" Next run tomo setup for both apps; this will set everything up for both users on the remote host (environment variables, rbenv, etc.). You can now deploy both apps to the same host, with the confidence that their configurations will be kept cleanly separated. Does tomo support git submodules? No, not out of the box. However, you can extend tomo with an additional task for submodules; see the solution in PR #220 suggested by @numbcoder . Support This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you\u2019d like to help by submitting a pull request, or if you\u2019ve discovered a bug that needs my attention, please let me know. Check out CONTRIBUTING.md to get started. Happy hacking! \u2014Matt License The gem is available as open source under the terms of the MIT License . Code of conduct Everyone interacting in the Tomo project\u2019s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct . Contribution guide Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short CONTRIBUTING.md guidelines before you dive in.","title":"Home"},{"location":"#tomo","text":"Tomo is a friendly command-line tool for deploying Rails apps. \ud83d\udcbb Rich command-line interface with built-in bash completions \u2601\ufe0f Multi-environment and role-based multi-host support \ud83d\udc8e Everything you need to deploy a basic Rails app out of the box \ud83d\udd0c Easily extensible for polyglot projects (not just Rails!) \ud83d\udcda Quality documentation \ud83d\udd2c Minimal dependencies \u2192 See how tomo compares to other Ruby deployment tools like Capistrano and Mina. Quick start Usage Extending tomo Tutorials Blog posts Reference documentation FAQ Support License Code of conduct Contribution guide","title":"Tomo"},{"location":"#quick-start","text":"","title":"Quick start"},{"location":"#installation","text":"Tomo is distributed as a ruby gem. To install: $ gem install tomo \ud83d\udca1 Protip: run tomo completion-script for instructions on setting up bash completions.","title":"Installation"},{"location":"#configuring-a-project","text":"Tomo is configured via a .tomo/config.rb file in your project. To get started, run tomo init to generate a configuration that works for a basic Rails app. An abbreviated version looks like this: # .tomo/config.rb plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"user@hostname.or.ip.address\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end","title":"Configuring a project"},{"location":"#next-steps","text":"\u2192 The reference docs have a complete guide to tomo configuration. \u2192 Check out the Deploying Rails From Scratch tutorial for a step-by-step guide to using tomo with a real app.","title":"Next steps"},{"location":"#usage","text":"Once your project is configured, you can: Run tomo setup to prepare the remote host for its first deploy. Run tomo deploy to deploy your app. Use tomo run to invoke one-off tasks, like launching a Rails console. \ud83d\udca1 Protip: add -h or --help when running any of these commands to see detailed docs and examples.","title":"Usage"},{"location":"#tomo-setup","text":"tomo setup prepares the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. Out of the box, tomo will: Configure necessary environment variables, like RAILS_ENV and SECRET_KEY_BASE Install Ruby, Bundler, Node, Yarn, and dependencies Create all necessary deployment directories Create the Rails database, load the schema, and insert seed data \u2192 Here is the default list of tasks invoked by the setup command. \u2192 The tomo setup section of the reference docs explains supported command-line options.","title":"tomo setup"},{"location":"#tomo-deploy","text":"Whereas tomo setup is typically run once, you can use tomo deploy every time you want to deploy a new version of your app. The deploy command will sequentially run the deploy list of tasks specified in .tomo/config.rb . You can customize this list to meet the needs of your app. By default, tomo runs these tasks: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) \ud83d\udca1 Protip: you can abbreviate tomo commands, like tomo d for tomo deploy or tomo s for tomo setup . \u2192 Here is the default list of tasks invoked by the deploy command. \u2192 The tomo deploy section of the reference docs explains supported command-line options, like --dry-run .","title":"tomo deploy"},{"location":"#tomo-run-task","text":"Tomo can also run individual remote tasks on demand. You can use the tasks command to see the list of tasks tomo knows about. One of the built-in Rails tasks is rails:console , which brings up a fully-interactive Rails console over SSH. \ud83d\udca1 Protip: you can shorten this as tomo rails:console (the run command is implied). \u2192 The tomo run section of the reference docs explains supported command-line options and has more examples.","title":"tomo run [TASK]"},{"location":"#extending-tomo","text":"Tomo has a powerful plugin system that lets you extend tomo by installing Ruby gems (e.g. tomo-plugin-sidekiq ). You can also define plugins on the fly within your project by adding simple .rb files to .tomo/plugins/ . These plugins can define tasks as plain ruby methods. For example: # .tomo/plugins/my-plugin.rb def hello remote.run \"echo\", \"hello\", settings[:application] end Load your plugin in config.rb like this: # .tomo/config.rb plugin \"./plugins/my-plugin.rb\" And run it! \u2192 The Writing Custom Tasks tutorial has an in-depth explanation of how plugins work. \u2192 The TaskLibrary API is tomo\u2019s DSL for building tasks. \u2192 The Publishing a Plugin tutorial explains how to package your plugin as a Ruby gem to share it with the community.","title":"Extending tomo"},{"location":"#tutorials","text":"Deploying Rails From Scratch Writing Custom Tasks Publishing a Plugin","title":"Tutorials"},{"location":"#blog-posts","text":"Automate Rails deployments with GitHub Actions","title":"Blog posts"},{"location":"#reference-documentation","text":"Configuration Commands init setup deploy run tasks Plugins core bundler env git nodenv puma rails rbenv API Host Logger Paths PluginDSL Remote Result TaskLibrary Testing::MockPluginTester","title":"Reference documentation"},{"location":"#faq","text":"","title":"FAQ"},{"location":"#what-does-the-unsupported-option-accept-new-error-mean","text":"By default, tomo uses the \u201caccept-new\u201d value for the StrictHostKeyChecking option, which is supported by OpenSSH 7.6 and newer. If you are using an older version, this will cause an error. As a workaround, you can override tomo\u2019s default behavior like this: # Replace \"accept-new\" with something compatible with older versions of SSH set ssh_strict_host_key_checking: true # or false","title":"What does the unsupported option \"accept-new\" error mean?"},{"location":"#can-i-deploy-multiple-apps-to-a-single-host","text":"Tomo relies on the host user\u2019s bash profile for various things, like setting environment variables and initializing rbenv and nodenv. This makes it impractical to deploy multiple apps to a single host using the same deploy user. The solution is to create multiple users on the remote host, and then configure a different user for deploying each app. That way each user can have its own distinct environment variables and you can easily configure each app differently without risking conflicts. Refer to the tomo Rails tutorial for instructions on creating a deploy user. E.g. app1 would be configured to deploy as: host \"app1@example.com\" And app2 would be configured to deploy as: host \"app2@example.com\" Next run tomo setup for both apps; this will set everything up for both users on the remote host (environment variables, rbenv, etc.). You can now deploy both apps to the same host, with the confidence that their configurations will be kept cleanly separated.","title":"Can I deploy multiple apps to a single host?"},{"location":"#does-tomo-support-git-submodules","text":"No, not out of the box. However, you can extend tomo with an additional task for submodules; see the solution in PR #220 suggested by @numbcoder .","title":"Does tomo support git submodules?"},{"location":"#support","text":"This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you\u2019d like to help by submitting a pull request, or if you\u2019ve discovered a bug that needs my attention, please let me know. Check out CONTRIBUTING.md to get started. Happy hacking! \u2014Matt","title":"Support"},{"location":"#license","text":"The gem is available as open source under the terms of the MIT License .","title":"License"},{"location":"#code-of-conduct","text":"Everyone interacting in the Tomo project\u2019s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct .","title":"Code of conduct"},{"location":"#contribution-guide","text":"Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short CONTRIBUTING.md guidelines before you dive in.","title":"Contribution guide"},{"location":"comparisons/","text":"Comparisons Tomo is a SSH-based deployment tool written in Ruby, and so it is natural to compare it with the many other popular Ruby tools in that category. Here are some specific design choices that make tomo different. Proven built-in Rails support. Tomo includes all the setup and deployment tasks you need to deploy a basic Rails app. On top of that, every release of tomo is automatically tested to verify that it can successfully deploy the latest versions of Rails, Bundler, and Ruby out of the box. Opinionated defaults. The tasks built-into tomo provide a very opinionated deployment: rbenv and nodenv to install ruby and node, puma via systemd with socket activation for zero-downtime restarts, 12-factor style configuration (environment variables as opposed to configuration files), and so on. Tomo provides a strong set of production-tested conventions without you needing to piece together snippets from blog posts or tutorials. Easy to extend. Unlike other Ruby deployment tools that layer DSL monkey patches on top of rake, tomo is built from the ground up with modern CLI conventions, testability, and simplicity in mind. Tomo takes care of SSH connection management for you, and the ordering of deployment and setup operations is purely configuration based. There are no complex chains of prerequisites or programmatic before/after hooks to deal with. All you have to do is write plain Ruby methods utilizing a concise API. Code example To get an idea of how much simpler tomo can be, here is an example of the same task implemented in capistrano vs tomo: # capistrano task :precompile do on release_roles(fetch(:assets_roles)) do within release_path do with rails_env: fetch(:rails_env), rails_groups: fetch(:rails_assets_groups) do execute :rake, \"assets:precompile\" end end end end # tomo def assets_precompile remote.rake(\"assets:precompile\") end Features Here\u2019s how tomo compares with the most popular Ruby-based deployment tools: Capistrano and Mina. Tomo Capistrano Mina First release 2019 2009 2012 Required gem dependencies 0 7 2 Minimum supported ruby version 3.1 2.0 2.0 Configuration files 1 (.tomo/config.rb) 3+ (Capfile, config/deploy.rb, config/deploy/*.rb per stage) 1 (config/deploy.rb) Deploy in parallel to multiple hosts \u2705 Yes \u2705 Yes \u274c No Configure multiple environments/stages \u2705 Yes \u2705 Yes \u2705 Yes, via custom rake tasks Simulate a deploy (dry run) \u2705 Yes \u2705 Yes \u2705 Yes Customizable deploy command \u2705 Yes, via config \u2705 Yes, via rake tasks that attach before/after hooks \u2705 Yes, via rake task Built-in setup or \u201ccold deploy\u201d command \u2705 Yes \u274c No \u2705 Yes Automated host setup (ruby, bundler, yarn, etc.) \u2705 Yes \u274c No \u274c No Rollback command \u274c No \u2705 Yes \u2705 Yes Manage host environment variables (SECRET_KEY_BASE, DATABASE_URL, etc.) \u2705 Yes \u274c No \u274c No Automated testing of deploying Rails end-to-end \u2705 Yes \u274c No \u274c No Built-in Rails web server tasks \u2705 Yes, puma with systemd \u274c No \u274c No Built-in ruby version manager tasks \u2705 Yes, rbenv \u274c No \u2705 Yes, chruby, rbenv, rvm, ry Built-in node version manager tasks \u2705 Yes, nodenv \u274c No \u274c No SSH command execution One at a time One at a time Batched SSH implementation Native Ruby (net-ssh) Native SSH connection management Automatic Explicit Automatic Task framework Tomo-specific Rake Rake Tasks can invoke other tasks \u274c No \u2705 Yes \u2705 Yes Tasks can modify settings on the fly \u274c No \u2705 Yes \u2705 Yes Tasks can have before/after hooks \u274c No \u2705 Yes, via DSL \u2705 Yes, via rake DSL availability Inside task definitions only Global mixin (monkey patch) Global mixin (monkey patch) DSL for running scripts locally (as opposed to on remote host) \u274c No \u2705 Yes \u2705 Yes DSL for uploading ERB templates \u2705 Yes \u274c No \u274c No","title":"Comparisons"},{"location":"comparisons/#comparisons","text":"Tomo is a SSH-based deployment tool written in Ruby, and so it is natural to compare it with the many other popular Ruby tools in that category. Here are some specific design choices that make tomo different. Proven built-in Rails support. Tomo includes all the setup and deployment tasks you need to deploy a basic Rails app. On top of that, every release of tomo is automatically tested to verify that it can successfully deploy the latest versions of Rails, Bundler, and Ruby out of the box. Opinionated defaults. The tasks built-into tomo provide a very opinionated deployment: rbenv and nodenv to install ruby and node, puma via systemd with socket activation for zero-downtime restarts, 12-factor style configuration (environment variables as opposed to configuration files), and so on. Tomo provides a strong set of production-tested conventions without you needing to piece together snippets from blog posts or tutorials. Easy to extend. Unlike other Ruby deployment tools that layer DSL monkey patches on top of rake, tomo is built from the ground up with modern CLI conventions, testability, and simplicity in mind. Tomo takes care of SSH connection management for you, and the ordering of deployment and setup operations is purely configuration based. There are no complex chains of prerequisites or programmatic before/after hooks to deal with. All you have to do is write plain Ruby methods utilizing a concise API.","title":"Comparisons"},{"location":"comparisons/#code-example","text":"To get an idea of how much simpler tomo can be, here is an example of the same task implemented in capistrano vs tomo: # capistrano task :precompile do on release_roles(fetch(:assets_roles)) do within release_path do with rails_env: fetch(:rails_env), rails_groups: fetch(:rails_assets_groups) do execute :rake, \"assets:precompile\" end end end end # tomo def assets_precompile remote.rake(\"assets:precompile\") end","title":"Code example"},{"location":"comparisons/#features","text":"Here\u2019s how tomo compares with the most popular Ruby-based deployment tools: Capistrano and Mina. Tomo Capistrano Mina First release 2019 2009 2012 Required gem dependencies 0 7 2 Minimum supported ruby version 3.1 2.0 2.0 Configuration files 1 (.tomo/config.rb) 3+ (Capfile, config/deploy.rb, config/deploy/*.rb per stage) 1 (config/deploy.rb) Deploy in parallel to multiple hosts \u2705 Yes \u2705 Yes \u274c No Configure multiple environments/stages \u2705 Yes \u2705 Yes \u2705 Yes, via custom rake tasks Simulate a deploy (dry run) \u2705 Yes \u2705 Yes \u2705 Yes Customizable deploy command \u2705 Yes, via config \u2705 Yes, via rake tasks that attach before/after hooks \u2705 Yes, via rake task Built-in setup or \u201ccold deploy\u201d command \u2705 Yes \u274c No \u2705 Yes Automated host setup (ruby, bundler, yarn, etc.) \u2705 Yes \u274c No \u274c No Rollback command \u274c No \u2705 Yes \u2705 Yes Manage host environment variables (SECRET_KEY_BASE, DATABASE_URL, etc.) \u2705 Yes \u274c No \u274c No Automated testing of deploying Rails end-to-end \u2705 Yes \u274c No \u274c No Built-in Rails web server tasks \u2705 Yes, puma with systemd \u274c No \u274c No Built-in ruby version manager tasks \u2705 Yes, rbenv \u274c No \u2705 Yes, chruby, rbenv, rvm, ry Built-in node version manager tasks \u2705 Yes, nodenv \u274c No \u274c No SSH command execution One at a time One at a time Batched SSH implementation Native Ruby (net-ssh) Native SSH connection management Automatic Explicit Automatic Task framework Tomo-specific Rake Rake Tasks can invoke other tasks \u274c No \u2705 Yes \u2705 Yes Tasks can modify settings on the fly \u274c No \u2705 Yes \u2705 Yes Tasks can have before/after hooks \u274c No \u2705 Yes, via DSL \u2705 Yes, via rake DSL availability Inside task definitions only Global mixin (monkey patch) Global mixin (monkey patch) DSL for running scripts locally (as opposed to on remote host) \u274c No \u2705 Yes \u2705 Yes DSL for uploading ERB templates \u2705 Yes \u274c No \u274c No","title":"Features"},{"location":"configuration/","text":"Configuration Tomo is configured via a .tomo/config.rb file. This configuration file defines what tasks to run when executing a setup or deploy , the settings that affect the behavior of those tasks, and the remote host or hosts where those tasks will be run. The format of tomo\u2019s configuration file is designed to be simple and concise for basic deployments, with the flexibility to scale to more advanced setups that involve multiple roles, environments, and hosts. A basic deployment will typically use these configuration directives: plugin host set setup deploy Here\u2019s an abbreviated example: plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"deployer@app.example.com\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end A more complex deployment may make use of these additional directives: environment role batch plugin(name_or_relative_path) Load a tomo plugin by name or from a Ruby file by a relative path. Several plugins are built into tomo: bundler , env , git , nodenv , puma , rails , and rbenv . If you want to use the tasks provided by one of these plugins, load it by name, like this: plugin \"git\" Plugins can also be provided by gems installed on your system. For example, the tomo-plugin-sidekiq gem provides the \u201csidekiq\u201d plugin. Make sure the gem is installed (e.g. in your Gemfile) and then reference the plugin by name to load it: plugin \"sidekiq\" Note that the name of the plugin may not necessarily match the name of the gem. Refer to the gem\u2019s documentation for installation instructions. Finally, if the argument to plugin starts with a dot ( . ) it is considered a relative path to a custom plugin. By convention, custom plugins are stored in .tomo/plugins/ within the project that tomo is deploying. The name of the plugin is inferred from its file name. So for example, if the plugin is loaded from a file named foo.rb , then the name of the plugin is \u201cfoo\u201d and all tasks it defines will be given the foo: namespace: plugin \"./plugins/foo.rb\" host(address, **options) Specify the SSH host address (including username) that tomo will connect to. For example: host \"deployer@app.example.com\" # port 22 is implied host \"admin@192.168.1.50\", port: 8022 # port 8022 The following advanced options are supported: Name Purpose Default port SSH port number. 22 roles An array of String roles to assign to this host. Used with the role directive for specifying which tasks should run on this host. [] log_prefix A String prefix to print next to all log output for this host. nil privileged_user The SSH user to connect as when running privileged tasks. See setup for an example. \"root\" set(hash) Specify a value for a tomo setting. For example, to change the number of releases that tomo retains when pruning old releases: set keep_releases: 5 For a full list of settings that affect tomo\u2019s core behavior, refer to the core plugin documentation . Each plugin such as bundler and git also has its own specialized list of settings. Refer to the each plugin\u2019s documentation for a full reference. Interpolation It is possible to reference other settings when specifying a value. The format of a reference string is %{name} where name is the name of another setting. This is often used to build paths that are relative to the release that is being deployed, or for paths relative to tomo\u2019s shared directory. In this example, the value will be interpolated to contain the release that is being deployed: set release_json_path: \"%{release_path}/.tomo_release.json\" # => \"/var/www/my-app/20190523234156/.tomo_release.json\" Another common use case is the shared directory: set bundler_path: \"%{shared_path}/bundle\" # => \"/var/www/my-app/shared/bundle\" Interpolation takes place after tomo has loaded all configuration, plugins, and overrides, just before tasks are run. Custom settings set will define a setting if it does not already exist. This means you can create arbitrarily-named settings for your own purposes, such as for use within custom tasks. set my_setting_i_just_made_up: \"great\" In practice most settings are strings, but any Ruby value is possible. set some_double: 0.57 set my_hash: { urgent: true, message: \"hello\" } Overrides Settings defined by set can be overridden when running a tomo command, e.g. tomo deploy , by way of environment variables and command-line arguments. Environment variable overrides take the form of TOMO_* . For example, this will override the :git_branch setting to be \u201cdevelop\u201d: $ export TOMO_GIT_BRANCH=develop $ tomo deploy On the command line, -s or --setting can be used. For example: $ tomo deploy -s git_branch=develop The precedence of overrides is as follows (higher in the list have higher precedence): Command-line overrides Environment variable overrides set Defaults (specified by plugins) setup(&block) Define the list of tasks that will be run by the tomo setup command, by providing a block containing run directives, like this: setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Each run can optionally take a privileged: true option. When specified, the task will be run using the \u201croot\u201d user instead of the default user specified for each host . setup do run \"apt:install\", privileged: true end deploy(&block) Define the list of tasks that will be run by the tomo deploy command, by providing a block containing run directives, like this: deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end environment(name, &block) Define an environment so that tomo can be used to deploy the same project with more than one set of configuration. Each environment must have a unique name and can contain its own host and set directives. For example: # Top-level config is shared by both environments set git_url: \"git@github.com:username/repo.git\" environment :staging do host \"deployer@staging.example.com\" set git_branch: \"develop\" end environment :production do host \"deployer@app.example.com\" set git_branch: \"main\" end Use the -e or --environment option when running tomo to select which environment to use. role(name, runs:) Specify that certain task(s) are only allowed to run on hosts that have the role name . The runs option must be an array of Strings representing task names. Simple wildcards (glob rules using * ) can be used to match multiple tasks. By default, every task that is listed in setup and deploy blocks is run on every host. In a multi-host deployment this is not always desirable. For example, the rails:db_seed and rails:db_migrate tasks should only be run once per deployment (i.e. on one host). To accomplish this, we can define a role named \u201cdb\u201d that is responsible for running these tasks, like this: role \"db\", runs: [\"rails:db_*\"] host \"deployer@app1.example.com\", roles: [\"db\"] host \"deployer@app2.example.com\", roles: [] The role directive in the example above tells tomo that any task matching the glob pattern rails:db_* should only run on hosts that are assigned the \u201cdb\u201d role. That means that app1.example.com will run rails:db_seed and rails:db_migrate , but app2.example.com will not. batch(&block) Define a group tasks to run in parallel during a multi-host deploy. This allows one host to \u201crace ahead\u201d of other hosts and leads to potentially faster deployments. In a multi-host deployment, by default each task in a setup and deploy must complete on all hosts before tomo will move onto the next task. This means a deployment is limited by its slowest host. If a task is configured via role to run on only one host (e.g. rails:db_migrate ), other hosts must wait until the task is done. We can speed this up by using batch , as in this example: deploy do # All tasks in this batch must complete before tomo will move onto # core:symlink_current, but within the batch each host can \"race ahead\" # independently in parallel. batch do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" end # This task must complete on all hosts before moving onto the next batch. run \"core:symlink_current\" # The tasks within this batch can run independently in parallel on each host. batch do run \"puma:restart\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end end At runtime, tomo turns this configuration into an \u201cexecution plan\u201d, which you can see by passing the --debug option to tomo deploy . Here\u2019s what the execution plan might look like for the above configuration with two hosts: DEBUG: Execution plan: CONCURRENTLY (2 THREADS): = CONNECT deployer@app1.example.com = CONNECT deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN env:update ON deployer@app1.example.com RUN git:create_release ON deployer@app1.example.com RUN core:symlink_shared ON deployer@app1.example.com RUN core:write_release_json ON deployer@app1.example.com RUN bundler:install ON deployer@app1.example.com RUN rails:assets_precompile ON deployer@app1.example.com RUN rails:db_migrate ON deployer@app1.example.com = IN SEQUENCE: RUN env:update ON deployer@app2.example.com RUN git:create_release ON deployer@app2.example.com RUN core:symlink_shared ON deployer@app2.example.com RUN core:write_release_json ON deployer@app2.example.com RUN bundler:install ON deployer@app2.example.com RUN rails:assets_precompile ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = RUN core:symlink_current ON deployer@app1.example.com = RUN core:symlink_current ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN puma:restart ON deployer@app1.example.com RUN core:clean_releases ON deployer@app1.example.com RUN bundler:clean ON deployer@app1.example.com RUN core:log_revision ON deployer@app1.example.com = IN SEQUENCE: RUN puma:restart ON deployer@app2.example.com RUN core:clean_releases ON deployer@app2.example.com RUN bundler:clean ON deployer@app2.example.com RUN core:log_revision ON deployer@app2.example.com As we can see, core:symlink_current is executed at the same time on both hosts, but before and after that, the other tasks can be executed out of sync.","title":"Configuration"},{"location":"configuration/#configuration","text":"Tomo is configured via a .tomo/config.rb file. This configuration file defines what tasks to run when executing a setup or deploy , the settings that affect the behavior of those tasks, and the remote host or hosts where those tasks will be run. The format of tomo\u2019s configuration file is designed to be simple and concise for basic deployments, with the flexibility to scale to more advanced setups that involve multiple roles, environments, and hosts. A basic deployment will typically use these configuration directives: plugin host set setup deploy Here\u2019s an abbreviated example: plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"deployer@app.example.com\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end A more complex deployment may make use of these additional directives: environment role batch","title":"Configuration"},{"location":"configuration/#pluginname_or_relative_path","text":"Load a tomo plugin by name or from a Ruby file by a relative path. Several plugins are built into tomo: bundler , env , git , nodenv , puma , rails , and rbenv . If you want to use the tasks provided by one of these plugins, load it by name, like this: plugin \"git\" Plugins can also be provided by gems installed on your system. For example, the tomo-plugin-sidekiq gem provides the \u201csidekiq\u201d plugin. Make sure the gem is installed (e.g. in your Gemfile) and then reference the plugin by name to load it: plugin \"sidekiq\" Note that the name of the plugin may not necessarily match the name of the gem. Refer to the gem\u2019s documentation for installation instructions. Finally, if the argument to plugin starts with a dot ( . ) it is considered a relative path to a custom plugin. By convention, custom plugins are stored in .tomo/plugins/ within the project that tomo is deploying. The name of the plugin is inferred from its file name. So for example, if the plugin is loaded from a file named foo.rb , then the name of the plugin is \u201cfoo\u201d and all tasks it defines will be given the foo: namespace: plugin \"./plugins/foo.rb\"","title":"plugin(name_or_relative_path)"},{"location":"configuration/#hostaddress-options","text":"Specify the SSH host address (including username) that tomo will connect to. For example: host \"deployer@app.example.com\" # port 22 is implied host \"admin@192.168.1.50\", port: 8022 # port 8022 The following advanced options are supported: Name Purpose Default port SSH port number. 22 roles An array of String roles to assign to this host. Used with the role directive for specifying which tasks should run on this host. [] log_prefix A String prefix to print next to all log output for this host. nil privileged_user The SSH user to connect as when running privileged tasks. See setup for an example. \"root\"","title":"host(address, **options)"},{"location":"configuration/#sethash","text":"Specify a value for a tomo setting. For example, to change the number of releases that tomo retains when pruning old releases: set keep_releases: 5 For a full list of settings that affect tomo\u2019s core behavior, refer to the core plugin documentation . Each plugin such as bundler and git also has its own specialized list of settings. Refer to the each plugin\u2019s documentation for a full reference.","title":"set(hash)"},{"location":"configuration/#interpolation","text":"It is possible to reference other settings when specifying a value. The format of a reference string is %{name} where name is the name of another setting. This is often used to build paths that are relative to the release that is being deployed, or for paths relative to tomo\u2019s shared directory. In this example, the value will be interpolated to contain the release that is being deployed: set release_json_path: \"%{release_path}/.tomo_release.json\" # => \"/var/www/my-app/20190523234156/.tomo_release.json\" Another common use case is the shared directory: set bundler_path: \"%{shared_path}/bundle\" # => \"/var/www/my-app/shared/bundle\" Interpolation takes place after tomo has loaded all configuration, plugins, and overrides, just before tasks are run.","title":"Interpolation"},{"location":"configuration/#custom-settings","text":"set will define a setting if it does not already exist. This means you can create arbitrarily-named settings for your own purposes, such as for use within custom tasks. set my_setting_i_just_made_up: \"great\" In practice most settings are strings, but any Ruby value is possible. set some_double: 0.57 set my_hash: { urgent: true, message: \"hello\" }","title":"Custom settings"},{"location":"configuration/#overrides","text":"Settings defined by set can be overridden when running a tomo command, e.g. tomo deploy , by way of environment variables and command-line arguments. Environment variable overrides take the form of TOMO_* . For example, this will override the :git_branch setting to be \u201cdevelop\u201d: $ export TOMO_GIT_BRANCH=develop $ tomo deploy On the command line, -s or --setting can be used. For example: $ tomo deploy -s git_branch=develop The precedence of overrides is as follows (higher in the list have higher precedence): Command-line overrides Environment variable overrides set Defaults (specified by plugins)","title":"Overrides"},{"location":"configuration/#setupblock","text":"Define the list of tasks that will be run by the tomo setup command, by providing a block containing run directives, like this: setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Each run can optionally take a privileged: true option. When specified, the task will be run using the \u201croot\u201d user instead of the default user specified for each host . setup do run \"apt:install\", privileged: true end","title":"setup(&block)"},{"location":"configuration/#deployblock","text":"Define the list of tasks that will be run by the tomo deploy command, by providing a block containing run directives, like this: deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end","title":"deploy(&block)"},{"location":"configuration/#environmentname-block","text":"Define an environment so that tomo can be used to deploy the same project with more than one set of configuration. Each environment must have a unique name and can contain its own host and set directives. For example: # Top-level config is shared by both environments set git_url: \"git@github.com:username/repo.git\" environment :staging do host \"deployer@staging.example.com\" set git_branch: \"develop\" end environment :production do host \"deployer@app.example.com\" set git_branch: \"main\" end Use the -e or --environment option when running tomo to select which environment to use.","title":"environment(name, &block)"},{"location":"configuration/#rolename-runs","text":"Specify that certain task(s) are only allowed to run on hosts that have the role name . The runs option must be an array of Strings representing task names. Simple wildcards (glob rules using * ) can be used to match multiple tasks. By default, every task that is listed in setup and deploy blocks is run on every host. In a multi-host deployment this is not always desirable. For example, the rails:db_seed and rails:db_migrate tasks should only be run once per deployment (i.e. on one host). To accomplish this, we can define a role named \u201cdb\u201d that is responsible for running these tasks, like this: role \"db\", runs: [\"rails:db_*\"] host \"deployer@app1.example.com\", roles: [\"db\"] host \"deployer@app2.example.com\", roles: [] The role directive in the example above tells tomo that any task matching the glob pattern rails:db_* should only run on hosts that are assigned the \u201cdb\u201d role. That means that app1.example.com will run rails:db_seed and rails:db_migrate , but app2.example.com will not.","title":"role(name, runs:)"},{"location":"configuration/#batchblock","text":"Define a group tasks to run in parallel during a multi-host deploy. This allows one host to \u201crace ahead\u201d of other hosts and leads to potentially faster deployments. In a multi-host deployment, by default each task in a setup and deploy must complete on all hosts before tomo will move onto the next task. This means a deployment is limited by its slowest host. If a task is configured via role to run on only one host (e.g. rails:db_migrate ), other hosts must wait until the task is done. We can speed this up by using batch , as in this example: deploy do # All tasks in this batch must complete before tomo will move onto # core:symlink_current, but within the batch each host can \"race ahead\" # independently in parallel. batch do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" end # This task must complete on all hosts before moving onto the next batch. run \"core:symlink_current\" # The tasks within this batch can run independently in parallel on each host. batch do run \"puma:restart\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end end At runtime, tomo turns this configuration into an \u201cexecution plan\u201d, which you can see by passing the --debug option to tomo deploy . Here\u2019s what the execution plan might look like for the above configuration with two hosts: DEBUG: Execution plan: CONCURRENTLY (2 THREADS): = CONNECT deployer@app1.example.com = CONNECT deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN env:update ON deployer@app1.example.com RUN git:create_release ON deployer@app1.example.com RUN core:symlink_shared ON deployer@app1.example.com RUN core:write_release_json ON deployer@app1.example.com RUN bundler:install ON deployer@app1.example.com RUN rails:assets_precompile ON deployer@app1.example.com RUN rails:db_migrate ON deployer@app1.example.com = IN SEQUENCE: RUN env:update ON deployer@app2.example.com RUN git:create_release ON deployer@app2.example.com RUN core:symlink_shared ON deployer@app2.example.com RUN core:write_release_json ON deployer@app2.example.com RUN bundler:install ON deployer@app2.example.com RUN rails:assets_precompile ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = RUN core:symlink_current ON deployer@app1.example.com = RUN core:symlink_current ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN puma:restart ON deployer@app1.example.com RUN core:clean_releases ON deployer@app1.example.com RUN bundler:clean ON deployer@app1.example.com RUN core:log_revision ON deployer@app1.example.com = IN SEQUENCE: RUN puma:restart ON deployer@app2.example.com RUN core:clean_releases ON deployer@app2.example.com RUN bundler:clean ON deployer@app2.example.com RUN core:log_revision ON deployer@app2.example.com As we can see, core:symlink_current is executed at the same time on both hosts, but before and after that, the other tasks can be executed out of sync.","title":"batch(&block)"},{"location":"api/Host/","text":"Tomo::Host Represents a remote SSH host. host.address # => \"example.com\" host.port # => 22 host.user # => \"deployer\" host.roles # => [\"app\", \"db\"] host.to_s # => \"deployer@example.com\" A Host is always frozen and cannot be modified. Instance methods address \u2192 String The host name or IP address. port \u2192 Integer The SSH port, usually 22. user \u2192 String The username used when connecting to the host via SSH. roles \u2192 [String] An array of roles that are assigned to this host. Roles are used in multi-host deployments to control which tasks are run on which hosts. to_s \u2192 String A representation of host in the form of user@address:port . If the port is 22, that portion is omitted.","title":"Tomo::Host"},{"location":"api/Host/#tomohost","text":"Represents a remote SSH host. host.address # => \"example.com\" host.port # => 22 host.user # => \"deployer\" host.roles # => [\"app\", \"db\"] host.to_s # => \"deployer@example.com\" A Host is always frozen and cannot be modified.","title":"Tomo::Host"},{"location":"api/Host/#instance-methods","text":"","title":"Instance methods"},{"location":"api/Host/#address-string","text":"The host name or IP address.","title":"address \u2192 String"},{"location":"api/Host/#port-integer","text":"The SSH port, usually 22.","title":"port \u2192 Integer"},{"location":"api/Host/#user-string","text":"The username used when connecting to the host via SSH.","title":"user \u2192 String"},{"location":"api/Host/#roles-string","text":"An array of roles that are assigned to this host. Roles are used in multi-host deployments to control which tasks are run on which hosts.","title":"roles \u2192 [String]"},{"location":"api/Host/#to_s-string","text":"A representation of host in the form of user@address:port . If the port is 22, that portion is omitted.","title":"to_s \u2192 String"},{"location":"api/Logger/","text":"Tomo::Logger Provides a simple interface for logging messages to stdout and stderr. In multi-host deployments, messages are automatically prefixed with [1] , [2] , etc. based on current host. This makes it easier to distinguish where log messages are coming from. $ tomo run bundler:clean tomo run v1.0.0 [1] \u2192 Connecting to deployer@web1.example.com [2] \u2192 Connecting to deployer@web2.example.com [1] \u2022 bundler:clean [2] \u2022 bundler:clean [1] cd /home/deployer/apps/my-app/current && bundle clean [2] cd /home/deployer/apps/my-app/current && bundle clean \u2714 Ran bundler:clean on deployer@web1.example.com and deployer@web2.example.com If tomo is run in --dry-run mode, log messages are prefixed with a * to indicate the commands are being simulated. $ tomo run bundler:clean --dry-run tomo run v1.0.0 * [1] \u2192 Connecting to deployer@web1.example.com * [2] \u2192 Connecting to deployer@web2.example.com * [1] \u2022 bundler:clean * [2] \u2022 bundler:clean * [1] cd /home/deployer/apps/my-app/current && bundle clean * [2] cd /home/deployer/apps/my-app/current && bundle clean * Simulated bundler:clean on deployer@web1.example.com and deployer@web2.example.com (dry run) Instance methods debug(message) \u2192 nil Prints a message to stderr in gray with a DEBUG: prefix. Debug messages are only shown if tomo is run with the --debug option. Otherwise this is a no-op. info(message) \u2192 nil Prints a message to stdout . warn(message) \u2192 nil Prints a message to stderr with a red WARNING: prefix. error(message) \u2192 nil Prints a message to stderr with a red ERROR: prefix, indented, and with leading and trailing blank lines for extra emphasis.","title":"Tomo::Logger"},{"location":"api/Logger/#tomologger","text":"Provides a simple interface for logging messages to stdout and stderr. In multi-host deployments, messages are automatically prefixed with [1] , [2] , etc. based on current host. This makes it easier to distinguish where log messages are coming from. $ tomo run bundler:clean tomo run v1.0.0 [1] \u2192 Connecting to deployer@web1.example.com [2] \u2192 Connecting to deployer@web2.example.com [1] \u2022 bundler:clean [2] \u2022 bundler:clean [1] cd /home/deployer/apps/my-app/current && bundle clean [2] cd /home/deployer/apps/my-app/current && bundle clean \u2714 Ran bundler:clean on deployer@web1.example.com and deployer@web2.example.com If tomo is run in --dry-run mode, log messages are prefixed with a * to indicate the commands are being simulated. $ tomo run bundler:clean --dry-run tomo run v1.0.0 * [1] \u2192 Connecting to deployer@web1.example.com * [2] \u2192 Connecting to deployer@web2.example.com * [1] \u2022 bundler:clean * [2] \u2022 bundler:clean * [1] cd /home/deployer/apps/my-app/current && bundle clean * [2] cd /home/deployer/apps/my-app/current && bundle clean * Simulated bundler:clean on deployer@web1.example.com and deployer@web2.example.com (dry run)","title":"Tomo::Logger"},{"location":"api/Logger/#instance-methods","text":"","title":"Instance methods"},{"location":"api/Logger/#debugmessage-nil","text":"Prints a message to stderr in gray with a DEBUG: prefix. Debug messages are only shown if tomo is run with the --debug option. Otherwise this is a no-op.","title":"debug(message) \u2192 nil"},{"location":"api/Logger/#infomessage-nil","text":"Prints a message to stdout .","title":"info(message) \u2192 nil"},{"location":"api/Logger/#warnmessage-nil","text":"Prints a message to stderr with a red WARNING: prefix.","title":"warn(message) \u2192 nil"},{"location":"api/Logger/#errormessage-nil","text":"Prints a message to stderr with a red ERROR: prefix, indented, and with leading and trailing blank lines for extra emphasis.","title":"error(message) \u2192 nil"},{"location":"api/Paths/","text":"Tomo::Paths Provides syntactic sugar for accessing settings that represent file system paths. For every tomo setting in the form :_path , Paths will expose a method of that name that behaves like a Ruby Pathname object. As a special exception, the :deploy_to setting is also exposed even though it does not follow the same naming convention. In tomo the following path settings are always available: settings[:deploy_to] # => \"/var/www/my-app\" settings[:current_path] # => \"/var/www/my-app/current\" settings[:release_path] # => \"/var/www/my-app/releases/20190531164322\" settings[:releases_path] # => \"/var/www/my-app/releases\" settings[:shared_path] # => \"/var/www/my-app/shared\" Using Paths, these same settings can be accessed like this: paths.deploy_to # => \"/var/www/my-app\" paths.current # => \"/var/www/my-app/current\" paths.release # => \"/var/www/my-app/releases/20190531164322\" paths.releases # => \"/var/www/my-app/releases\" paths.shared # => \"/var/www/my-app/shared\" More powerfully, the values returned by Paths respond to join and dirname , so you can easily compose them: paths.current.dirname # => \"/var/www/my-app\" paths.release.join(\"tmp\") # => \"/var/www/my-app/releases/20190531164322/tmp\" paths.shared.join(\"bundle\") # => \"/var/www/my-app/shared/bundle\" Paths can be used wherever a path string is expected, like chdir : remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"puma\", \"--daemon\") end # $ cd /var/www/my-app/current && bundle exec puma --daemon If a plugin defines a setting with the suffix _path or if you create your own setting with that suffix, it automatically will be exposed via the Paths object: # .tomo/config.rb set my_custom_path: \"/opt/custom\" paths.my_custom.join(\"var\") # => \"/opt/custom/var\"","title":"Tomo::Paths"},{"location":"api/Paths/#tomopaths","text":"Provides syntactic sugar for accessing settings that represent file system paths. For every tomo setting in the form :_path , Paths will expose a method of that name that behaves like a Ruby Pathname object. As a special exception, the :deploy_to setting is also exposed even though it does not follow the same naming convention. In tomo the following path settings are always available: settings[:deploy_to] # => \"/var/www/my-app\" settings[:current_path] # => \"/var/www/my-app/current\" settings[:release_path] # => \"/var/www/my-app/releases/20190531164322\" settings[:releases_path] # => \"/var/www/my-app/releases\" settings[:shared_path] # => \"/var/www/my-app/shared\" Using Paths, these same settings can be accessed like this: paths.deploy_to # => \"/var/www/my-app\" paths.current # => \"/var/www/my-app/current\" paths.release # => \"/var/www/my-app/releases/20190531164322\" paths.releases # => \"/var/www/my-app/releases\" paths.shared # => \"/var/www/my-app/shared\" More powerfully, the values returned by Paths respond to join and dirname , so you can easily compose them: paths.current.dirname # => \"/var/www/my-app\" paths.release.join(\"tmp\") # => \"/var/www/my-app/releases/20190531164322/tmp\" paths.shared.join(\"bundle\") # => \"/var/www/my-app/shared/bundle\" Paths can be used wherever a path string is expected, like chdir : remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"puma\", \"--daemon\") end # $ cd /var/www/my-app/current && bundle exec puma --daemon If a plugin defines a setting with the suffix _path or if you create your own setting with that suffix, it automatically will be exposed via the Paths object: # .tomo/config.rb set my_custom_path: \"/opt/custom\" paths.my_custom.join(\"var\") # => \"/opt/custom/var\"","title":"Tomo::Paths"},{"location":"api/PluginDSL/","text":"Tomo::PluginDSL A tomo plugin is defined by a Ruby module that extends Tomo::PluginDSL. A plugin definition can specify three things: Default settings Tasks Helpers Here is the bundler plugin as an example: require_relative \"bundler/helpers\" require_relative \"bundler/tasks\" module Tomo::Plugin::Bundler extend Tomo::PluginDSL tasks Tomo::Plugin::Bundler::Tasks helpers Tomo::Plugin::Bundler::Helpers defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end The above plugin defines several default settings, defines tasks using a TaskLibrary named Tomo::Plugin::Bundler::Tasks , and defines helpers in a module named Tomo::Plugin::Bundler::Helpers . Refer to the Publishing a Plugin tutorial for more information about packaging and distributing tomo plugins. Instance methods defaults(hash) Specify default settings that will be applied when this plugin is loaded. Although not strictly necessary, it is best practice to list all required and optional settings that are used by the plugin, even if the default values are nil . This lets other developers know what setting names are expected when using the plugin. Settings must use symbol keys and typically String values, although any Ruby type is possible. Strings can contain interpolated values . module Tomo::Plugin::Bundler extend Tomo::PluginDSL defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end tasks(*task_library_class) Specify the tasks that will be defined by this plugin by supplying one or more TaskLibrary classes. The public instance methods of each class will be turned into tomo tasks. class Tomo::Plugin::Git::Tasks < Tomo::TaskLibrary def clone # ... end def create_release # ... end end class Tomo::Plugin::Git extend Tomo::PluginDSL tasks Tomo::Plugin::Git::Tasks end You can use tasks self to define a plugin and its tasks together as a single class: class Tomo::Plugin::Git < Tomo::TaskLibrary extend Tomo::PluginDSL tasks self def clone # ... end def create_release # ... end end helpers(*module) Specify the helpers that will be defined by this plugin by supplying one or more plain Ruby modules. The modules will be mixed in at runtime to extend the Remote interface with additional methods. module Tomo::Plugin::Core::Helpers def ln_sf(target, link, **run_opts) # ... end def mkdir_p(*directories, **run_opts) # ... end end module Tomo::Plugin::Core extend Tomo::PluginDSL helpers Tomo::Plugin::Core::Helpers end","title":"Tomo::PluginDSL"},{"location":"api/PluginDSL/#tomoplugindsl","text":"A tomo plugin is defined by a Ruby module that extends Tomo::PluginDSL. A plugin definition can specify three things: Default settings Tasks Helpers Here is the bundler plugin as an example: require_relative \"bundler/helpers\" require_relative \"bundler/tasks\" module Tomo::Plugin::Bundler extend Tomo::PluginDSL tasks Tomo::Plugin::Bundler::Tasks helpers Tomo::Plugin::Bundler::Helpers defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end The above plugin defines several default settings, defines tasks using a TaskLibrary named Tomo::Plugin::Bundler::Tasks , and defines helpers in a module named Tomo::Plugin::Bundler::Helpers . Refer to the Publishing a Plugin tutorial for more information about packaging and distributing tomo plugins.","title":"Tomo::PluginDSL"},{"location":"api/PluginDSL/#instance-methods","text":"","title":"Instance methods"},{"location":"api/PluginDSL/#defaultshash","text":"Specify default settings that will be applied when this plugin is loaded. Although not strictly necessary, it is best practice to list all required and optional settings that are used by the plugin, even if the default values are nil . This lets other developers know what setting names are expected when using the plugin. Settings must use symbol keys and typically String values, although any Ruby type is possible. Strings can contain interpolated values . module Tomo::Plugin::Bundler extend Tomo::PluginDSL defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end","title":"defaults(hash)"},{"location":"api/PluginDSL/#taskstask_library_class","text":"Specify the tasks that will be defined by this plugin by supplying one or more TaskLibrary classes. The public instance methods of each class will be turned into tomo tasks. class Tomo::Plugin::Git::Tasks < Tomo::TaskLibrary def clone # ... end def create_release # ... end end class Tomo::Plugin::Git extend Tomo::PluginDSL tasks Tomo::Plugin::Git::Tasks end You can use tasks self to define a plugin and its tasks together as a single class: class Tomo::Plugin::Git < Tomo::TaskLibrary extend Tomo::PluginDSL tasks self def clone # ... end def create_release # ... end end","title":"tasks(*task_library_class)"},{"location":"api/PluginDSL/#helpersmodule","text":"Specify the helpers that will be defined by this plugin by supplying one or more plain Ruby modules. The modules will be mixed in at runtime to extend the Remote interface with additional methods. module Tomo::Plugin::Core::Helpers def ln_sf(target, link, **run_opts) # ... end def mkdir_p(*directories, **run_opts) # ... end end module Tomo::Plugin::Core extend Tomo::PluginDSL helpers Tomo::Plugin::Core::Helpers end","title":"helpers(*module)"},{"location":"api/Remote/","text":"Tomo::Remote A Remote represents an SSH connection to a remote host and provides a fa\u00e7ade 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) \u2192 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] # => \" is safe & easy\" remote.run \"echo\", settings[:greeting] # $ echo \\\\ 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\u2019s default login directory (almost always this is the user\u2019s 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 \u201cattach\u201d 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) \u2192 obj Changes into the specified dir when executing a script via run or attach . Must be used with a block. This causes cd && 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) \u2192 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) \u2192 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) \u2192 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 \u2192 Tomo::Host The remote SSH host that scripts will be run on. release \u2192 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","title":"Tomo::Remote"},{"location":"api/Remote/#tomoremote","text":"A Remote represents an SSH connection to a remote host and provides a fa\u00e7ade 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","title":"Tomo::Remote"},{"location":"api/Remote/#instance-methods","text":"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.","title":"Instance methods"},{"location":"api/Remote/#runcommand-options-tomoresult","text":"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] # => \" is safe & easy\" remote.run \"echo\", settings[:greeting] # $ echo \\\\ 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\u2019s default login directory (almost always this is the user\u2019s 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","title":"run(*command, **options) \u2192 Tomo::Result"},{"location":"api/Remote/#attachcommand-options","text":"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 \u201cattach\u201d 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).","title":"attach(*command, **options)"},{"location":"api/Remote/#chdirdir-block-obj","text":"Changes into the specified dir when executing a script via run or attach . Must be used with a block. This causes cd && 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.","title":"chdir(dir, &block) \u2192 obj"},{"location":"api/Remote/#envhash-block-obj","text":"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.","title":"env(hash, &block) \u2192 obj"},{"location":"api/Remote/#prependcommand-block-obj","text":"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.","title":"prepend(*command, &block) \u2192 obj"},{"location":"api/Remote/#umaskmask-block-obj","text":"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.","title":"umask(mask, &block) \u2192 obj"},{"location":"api/Remote/#host-tomohost","text":"The remote SSH host that scripts will be run on.","title":"host \u2192 Tomo::Host"},{"location":"api/Remote/#release-hash","text":"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","title":"release \u2192 Hash"},{"location":"api/Result/","text":"Tomo::Result Represents the result of a remote SSH script. result = remote.run(\"echo\", \"hello world\") result.success? # => true result.failure? # => false result.exit_status # => 0 result.stdout # => \"hello world\\n\" result.stderr # => \"\" result.output # => \"hello world\\n\" A Result is always frozen and cannot be modified. Instance methods success? \u2192 true or false Whether the remote SSH script executed successfully. An exit status of 0 is considered success. failure? \u2192 true or false Whether the remote SSH script failed to execute. An non-zero exit status is considered a failure. exit_status \u2192 Integer The exit status returned by the remote SSH script. A status of 0 is considered success. stdout \u2192 String All data that was written to stdout by the remote SSH script. Empty string if nothing was written. stderr \u2192 String All data that was written to stderr by the remote SSH script. Empty string if nothing was written. output \u2192 String All data that was written by the remote SSH script: stdout and stderr combined, in that order. Empty string if nothing was written.","title":"Tomo::Result"},{"location":"api/Result/#tomoresult","text":"Represents the result of a remote SSH script. result = remote.run(\"echo\", \"hello world\") result.success? # => true result.failure? # => false result.exit_status # => 0 result.stdout # => \"hello world\\n\" result.stderr # => \"\" result.output # => \"hello world\\n\" A Result is always frozen and cannot be modified.","title":"Tomo::Result"},{"location":"api/Result/#instance-methods","text":"","title":"Instance methods"},{"location":"api/Result/#success-true-or-false","text":"Whether the remote SSH script executed successfully. An exit status of 0 is considered success.","title":"success? \u2192 true or false"},{"location":"api/Result/#failure-true-or-false","text":"Whether the remote SSH script failed to execute. An non-zero exit status is considered a failure.","title":"failure? \u2192 true or false"},{"location":"api/Result/#exit_status-integer","text":"The exit status returned by the remote SSH script. A status of 0 is considered success.","title":"exit_status \u2192 Integer"},{"location":"api/Result/#stdout-string","text":"All data that was written to stdout by the remote SSH script. Empty string if nothing was written.","title":"stdout \u2192 String"},{"location":"api/Result/#stderr-string","text":"All data that was written to stderr by the remote SSH script. Empty string if nothing was written.","title":"stderr \u2192 String"},{"location":"api/Result/#output-string","text":"All data that was written by the remote SSH script: stdout and stderr combined, in that order. Empty string if nothing was written.","title":"output \u2192 String"},{"location":"api/TaskLibrary/","text":"Tomo::TaskLibrary This is the primary public API for extending tomo. A TaskLibrary defines tasks. Every public instance method of a TaskLibrary becomes accessible to tomo as a task of the same name, prefixed by the name of its plugin. For example, this is how the git:clone task is defined: module Tomo::Plugin::Git class Tasks < Tomo::TaskLibrary # This becomes the implementation of the git:clone task def clone require_setting :git_url # ... end end end The TaskLibrary base class provides several useful private methods (detailed below) that allow task authors to run commands on the remote host, access tomo settings, and more. For more information on writing tasks, refer to the Writing Custom Tasks tutorial. Instance methods paths \u2192 Tomo::Paths Returns a Paths object that provides convenient access to settings representing file system paths. paths.current.join(\"lib\") # => \"/var/www/my-app/current/lib\" # ...which is syntactic sugar for: Pathname.new(settings[:current_path]).join(\"lib\") settings \u2192 Hash Returns a frozen (i.e. read-only) Hash containing all of tomo\u2019s settings. Any string interpolations will have already been applied. The keys representing the setting names are always symbols. settings[:application] # => \"my-app\" settings[:deploy_to] # => \"/var/www/my-app\" settings[:non_existing] # => nil settings.fetch(:non_existing) # => KeyError settings[:foo] = \"bar\" # => FrozenError settings.key?(:application) # => true settings.key?(:non_existing) # => false remote \u2192 Tomo::Remote Returns the Remote fa\u00e7ade that allows scripts to be run on the remote host. remote.run(\"echo\", \"hello world\") require_setting(name) \u2192 nil Raises an exception if a setting with the given name is not present. In other words, it will raise if settings[name] is nil . This can be used as a guard clause to ensure that users provide all necessary settings before a task can be run. def clone require_setting :git_url remote.run \"git\", \"clone\", settings[:git_url] end require_settings(*names) \u2192 nil Like require_setting , except it accepts an arbitrary number of setting names. Raises if any of the settings are nil . require_settings :puma_control_token, :puma_control_url merge_template(path) \u2192 String Given a local path to an ERB template, merge that template and return the resulting string. The ERB template can access the same API that tasks and helpers can access, namely: settings , paths , remote , and raw . Here is an example of an ERB template: Hello, <%= settings[:application] %>! If path begins with a \".\" it is interpreted as a path relative to the tomo configuration file. This allows for easy reference to project-specific templates. For example, given this directory structure: .tomo \u251c\u2500\u2500 config.rb \u2514\u2500\u2500 templates \u2514\u2500\u2500 unicorn.service.erb Then you could reference the template in a setting like this: # .tomo/config.rb set unicorn_service_template_path: \"./templates/unicorn.service.erb\" And merge it in a task: merge_template(paths.unicorn_service_template) dry_run? \u2192 true or false Returns true if tomo was started with the --dry-run option. This is useful if there are certain code paths you want to ensure are taken during a dry run. def install return if remote.bundle?(\"check\", *check_options) && !dry_run? remote.bundle(\"install\", *install_options) end logger \u2192 Tomo::Logger Returns the global Logger object that can be used to write messages to tomo\u2019s output. logger.debug \"got here\" logger.info \"hi!\" logger.warn \"uh oh\" die(reason) Immediately halt task execution by raising an exception. This will automatically print information to stderr about what task failed, on which host, and the reason for the failure. raw(string) \u2192 String Mark a string as a \u201craw\u201d value so that it is not automatically escaped. By default tomo applies shell escaping rules for safety. If you explicitly want to invoke shell behavior, use raw to prevent these escaping rules. remote.run \"ls\", \"$HOME/.bashrc\" # $ ls $\\HOME/.bashrc # \"$HOME/.bashrc\": No such file or directory (os error 2) remote.run \"ls\", raw(\"$HOME/.bashrc\") # $ ls $HOME/.bashrc # /home/deployer/.bashrc","title":"Tomo::TaskLibrary"},{"location":"api/TaskLibrary/#tomotasklibrary","text":"This is the primary public API for extending tomo. A TaskLibrary defines tasks. Every public instance method of a TaskLibrary becomes accessible to tomo as a task of the same name, prefixed by the name of its plugin. For example, this is how the git:clone task is defined: module Tomo::Plugin::Git class Tasks < Tomo::TaskLibrary # This becomes the implementation of the git:clone task def clone require_setting :git_url # ... end end end The TaskLibrary base class provides several useful private methods (detailed below) that allow task authors to run commands on the remote host, access tomo settings, and more. For more information on writing tasks, refer to the Writing Custom Tasks tutorial.","title":"Tomo::TaskLibrary"},{"location":"api/TaskLibrary/#instance-methods","text":"","title":"Instance methods"},{"location":"api/TaskLibrary/#paths-tomopaths","text":"Returns a Paths object that provides convenient access to settings representing file system paths. paths.current.join(\"lib\") # => \"/var/www/my-app/current/lib\" # ...which is syntactic sugar for: Pathname.new(settings[:current_path]).join(\"lib\")","title":"paths \u2192 Tomo::Paths"},{"location":"api/TaskLibrary/#settings-hash","text":"Returns a frozen (i.e. read-only) Hash containing all of tomo\u2019s settings. Any string interpolations will have already been applied. The keys representing the setting names are always symbols. settings[:application] # => \"my-app\" settings[:deploy_to] # => \"/var/www/my-app\" settings[:non_existing] # => nil settings.fetch(:non_existing) # => KeyError settings[:foo] = \"bar\" # => FrozenError settings.key?(:application) # => true settings.key?(:non_existing) # => false","title":"settings \u2192 Hash"},{"location":"api/TaskLibrary/#remote-tomoremote","text":"Returns the Remote fa\u00e7ade that allows scripts to be run on the remote host. remote.run(\"echo\", \"hello world\")","title":"remote \u2192 Tomo::Remote"},{"location":"api/TaskLibrary/#require_settingname-nil","text":"Raises an exception if a setting with the given name is not present. In other words, it will raise if settings[name] is nil . This can be used as a guard clause to ensure that users provide all necessary settings before a task can be run. def clone require_setting :git_url remote.run \"git\", \"clone\", settings[:git_url] end","title":"require_setting(name) \u2192 nil"},{"location":"api/TaskLibrary/#require_settingsnames-nil","text":"Like require_setting , except it accepts an arbitrary number of setting names. Raises if any of the settings are nil . require_settings :puma_control_token, :puma_control_url","title":"require_settings(*names) \u2192 nil"},{"location":"api/TaskLibrary/#merge_templatepath-string","text":"Given a local path to an ERB template, merge that template and return the resulting string. The ERB template can access the same API that tasks and helpers can access, namely: settings , paths , remote , and raw . Here is an example of an ERB template: Hello, <%= settings[:application] %>! If path begins with a \".\" it is interpreted as a path relative to the tomo configuration file. This allows for easy reference to project-specific templates. For example, given this directory structure: .tomo \u251c\u2500\u2500 config.rb \u2514\u2500\u2500 templates \u2514\u2500\u2500 unicorn.service.erb Then you could reference the template in a setting like this: # .tomo/config.rb set unicorn_service_template_path: \"./templates/unicorn.service.erb\" And merge it in a task: merge_template(paths.unicorn_service_template)","title":"merge_template(path) \u2192 String"},{"location":"api/TaskLibrary/#dry_run-true-or-false","text":"Returns true if tomo was started with the --dry-run option. This is useful if there are certain code paths you want to ensure are taken during a dry run. def install return if remote.bundle?(\"check\", *check_options) && !dry_run? remote.bundle(\"install\", *install_options) end","title":"dry_run? \u2192 true or false"},{"location":"api/TaskLibrary/#logger-tomologger","text":"Returns the global Logger object that can be used to write messages to tomo\u2019s output. logger.debug \"got here\" logger.info \"hi!\" logger.warn \"uh oh\"","title":"logger \u2192 Tomo::Logger"},{"location":"api/TaskLibrary/#diereason","text":"Immediately halt task execution by raising an exception. This will automatically print information to stderr about what task failed, on which host, and the reason for the failure.","title":"die(reason)"},{"location":"api/TaskLibrary/#rawstring-string","text":"Mark a string as a \u201craw\u201d value so that it is not automatically escaped. By default tomo applies shell escaping rules for safety. If you explicitly want to invoke shell behavior, use raw to prevent these escaping rules. remote.run \"ls\", \"$HOME/.bashrc\" # $ ls $\\HOME/.bashrc # \"$HOME/.bashrc\": No such file or directory (os error 2) remote.run \"ls\", raw(\"$HOME/.bashrc\") # $ ls $HOME/.bashrc # /home/deployer/.bashrc","title":"raw(string) \u2192 String"},{"location":"api/testing/MockPluginTester/","text":"Tomo::Testing::MockPluginTester MockPluginTester is a helper object that allows tasks and helpers provided by plugins to be easily unit tested. It has no test framework dependencies so it can be used in Minitest, RSpec, or the testing framework of your choice. MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. For example: require \"tomo/testing\" def test_setup_directories tester = Tomo::Testing::MockPluginTester.new(settings: { deploy_to: \"/app\" }) tester.run_task(\"core:setup_directories\") assert_equal(\"mkdir -p /app /app/releases /app/shared\", tester.executed_script) end You can change the default mocking behavior by using mock_script_result , like this: require \"tomo/testing\" def test_install tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts ) end Every MockPluginTester instance loads a fresh, independent tomo environment, so mocks, plugins, settings, etc. specified in one tester will not affect any other tests. Note that you must require \"tomo/testing\" to use MockPluginTester. Class methods new(*plugin_names, settings: {}, release: {}) \u2192 new_tester Build a new MockPluginTester that loads the given list of plugin_names . The resulting tester object can be used to simulate any tasks or helpers that are provided by these plugins. Note that the \u201ccore\u201d plugin is always loaded implicitly and does not need to be specified. Any settings that are specified will be applied after the defaults settings provided by the plugins have been defined. These settings can use template strings just like set . require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"puma\", settings: { application: \"test\", current_path: \"/app/current\" } ) Any release data specified will be available to the task under test via remote.release . Instance methods run_task(task, *args) \u2192 nil Run the given task by its fully qualified name (the namespace is required). Any args , if specified, are passed to the task via settings[:run_args] . Any remote SSH scripts run by the task (e.g. via remote.run ) will be mocked according to rules previously supplied to mock_script_result . If a mock result has not been explicitly supplied, the script will use a default mock that returns a successful result with no output. require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts ) call_helper(helper, *args, **kwargs) \u2192 obj Invoke the specified helper method name with the optional positional args and keyword kwargs . Returns the return value of the helper. Remote SSH scripts are mocked as explained in run_task . require \"tomo/testing\" def test_capture_returns_stdout_not_stderr tester = Tomo::Testing::MockPluginTester.new tester.mock_script_result(stderr: \"oh no\", stdout: \"hello world\\n\") captured = tester.call_helper(:capture, \"greet\") assert_equal(\"hello world\\n\", captured) end mock_script_result(script=/.*/, stdout: \u201c\u201d, stderr: \u201c\u201d, exit_status: 0) \u2192 self Mock the return value of remote SSH scripts that match the given script . If script is a String, the mock rule will apply only to scripts that match this String exactly. If script is Regexp, the mock rule will apply to any scripts that match that pattern. If script is omitted, the mock rule will apply always. In this example, any task or helper invoked via this tester that runs readline /app/current will receive the given mock stdout response: tester.mock_script_result(\"readlink /app/current\", stdout: <<~OUT) /app/releases/20190420203028 OUT Here, any script that includes systemctl will fail with an exit status of 1: tester.mock_script_result(/systemctl/, exit_status: 1) This mocks all scripts to fail with exit status of 255 and stderr of \u201coh no!\u201d: tester.mock_script_result(exit_status: 255, stderr: \"oh no!\") executed_script \u2192 String The remote SSH script that was run by a previous invocation of run_task or call_helper . If no script was run, then nil is returned. If more that one script was run, this will raise a RuntimeError . executed_scripts \u2192 [String] All remote SSH scripts that were run by a previous invocation of run_task or call_helper . If no script was run, then an empty array is returned. stdout \u2192 String Everything that was written to stdout during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned. stderr \u2192 String Everything that was written to stderr during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned.","title":"Tomo::Testing::MockPluginTester"},{"location":"api/testing/MockPluginTester/#tomotestingmockplugintester","text":"MockPluginTester is a helper object that allows tasks and helpers provided by plugins to be easily unit tested. It has no test framework dependencies so it can be used in Minitest, RSpec, or the testing framework of your choice. MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. For example: require \"tomo/testing\" def test_setup_directories tester = Tomo::Testing::MockPluginTester.new(settings: { deploy_to: \"/app\" }) tester.run_task(\"core:setup_directories\") assert_equal(\"mkdir -p /app /app/releases /app/shared\", tester.executed_script) end You can change the default mocking behavior by using mock_script_result , like this: require \"tomo/testing\" def test_install tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts ) end Every MockPluginTester instance loads a fresh, independent tomo environment, so mocks, plugins, settings, etc. specified in one tester will not affect any other tests. Note that you must require \"tomo/testing\" to use MockPluginTester.","title":"Tomo::Testing::MockPluginTester"},{"location":"api/testing/MockPluginTester/#class-methods","text":"","title":"Class methods"},{"location":"api/testing/MockPluginTester/#newplugin_names-settings-release-new_tester","text":"Build a new MockPluginTester that loads the given list of plugin_names . The resulting tester object can be used to simulate any tasks or helpers that are provided by these plugins. Note that the \u201ccore\u201d plugin is always loaded implicitly and does not need to be specified. Any settings that are specified will be applied after the defaults settings provided by the plugins have been defined. These settings can use template strings just like set . require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"puma\", settings: { application: \"test\", current_path: \"/app/current\" } ) Any release data specified will be available to the task under test via remote.release .","title":"new(*plugin_names, settings: {}, release: {}) \u2192 new_tester"},{"location":"api/testing/MockPluginTester/#instance-methods","text":"","title":"Instance methods"},{"location":"api/testing/MockPluginTester/#run_tasktask-args-nil","text":"Run the given task by its fully qualified name (the namespace is required). Any args , if specified, are passed to the task via settings[:run_args] . Any remote SSH scripts run by the task (e.g. via remote.run ) will be mocked according to rules previously supplied to mock_script_result . If a mock result has not been explicitly supplied, the script will use a default mock that returns a successful result with no output. require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts )","title":"run_task(task, *args) \u2192 nil"},{"location":"api/testing/MockPluginTester/#call_helperhelper-args-kwargs-obj","text":"Invoke the specified helper method name with the optional positional args and keyword kwargs . Returns the return value of the helper. Remote SSH scripts are mocked as explained in run_task . require \"tomo/testing\" def test_capture_returns_stdout_not_stderr tester = Tomo::Testing::MockPluginTester.new tester.mock_script_result(stderr: \"oh no\", stdout: \"hello world\\n\") captured = tester.call_helper(:capture, \"greet\") assert_equal(\"hello world\\n\", captured) end","title":"call_helper(helper, *args, **kwargs) \u2192 obj"},{"location":"api/testing/MockPluginTester/#mock_script_resultscript-stdout-stderr-exit_status-0-self","text":"Mock the return value of remote SSH scripts that match the given script . If script is a String, the mock rule will apply only to scripts that match this String exactly. If script is Regexp, the mock rule will apply to any scripts that match that pattern. If script is omitted, the mock rule will apply always. In this example, any task or helper invoked via this tester that runs readline /app/current will receive the given mock stdout response: tester.mock_script_result(\"readlink /app/current\", stdout: <<~OUT) /app/releases/20190420203028 OUT Here, any script that includes systemctl will fail with an exit status of 1: tester.mock_script_result(/systemctl/, exit_status: 1) This mocks all scripts to fail with exit status of 255 and stderr of \u201coh no!\u201d: tester.mock_script_result(exit_status: 255, stderr: \"oh no!\")","title":"mock_script_result(script=/.*/, stdout: “”, stderr: “”, exit_status: 0) \u2192 self"},{"location":"api/testing/MockPluginTester/#executed_script-string","text":"The remote SSH script that was run by a previous invocation of run_task or call_helper . If no script was run, then nil is returned. If more that one script was run, this will raise a RuntimeError .","title":"executed_script \u2192 String"},{"location":"api/testing/MockPluginTester/#executed_scripts-string","text":"All remote SSH scripts that were run by a previous invocation of run_task or call_helper . If no script was run, then an empty array is returned.","title":"executed_scripts \u2192 [String]"},{"location":"api/testing/MockPluginTester/#stdout-string","text":"Everything that was written to stdout during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned.","title":"stdout \u2192 String"},{"location":"api/testing/MockPluginTester/#stderr-string","text":"Everything that was written to stderr during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned.","title":"stderr \u2192 String"},{"location":"commands/deploy/","text":"deploy Deploy the current project to remote host(s). Usage $ tomo deploy [--dry-run] [options] Sequentially run the deploy list of tasks specified in .tomo/config.rb to deploy the project to a remote host. In practice, a deploy will usually consist of the following steps: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) During a deploy, tomo will initialize the :release_path setting based on the current date and time (e.g. /var/www/my-app/releases/20190616214752 ). Any tasks that copy files into a release ( git:create_release ), or run inside the release ( bundler:install , rails:assets_precompile , rails:db_migrate , etc.) will operate using this path. As a result, every tomo deploy will create a new entry in the releases directory, and the current symlink will point to the release that is currently active, i.e. the most recent successful deploy. The directory structure on the remote host looks like this: /var/www/my-app \u251c\u2500\u2500 git_repo/ \u251c\u2500\u2500 releases/ | \u251c\u2500\u2500 20190614192115/ | \u251c\u2500\u2500 20190615034736/ | \u2514\u2500\u2500 20190616214752/ \u251c\u2500\u2500 shared/ | \u251c\u2500\u2500 bundle/ | \u251c\u2500\u2500 log/ | \u251c\u2500\u2500 node_modules/ | \u2514\u2500\u2500 public/ | \u2514\u2500\u2500 assets/ \u251c\u2500\u2500 current -> /var/www/my-app/releases/20190616214752 \u2514\u2500\u2500 revisions.log This structure is customizable; see the core plugin settings for details. Tip: run tomo deploy --debug --dry-run to see an in-depth explanation of the settings and execution plan that will be used for the deployment. Options Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example Given the following configuration: host \"deployer@localhost\", port: 32809 deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end Then a deploy would produce: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:update \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /var/www/rails-new/releases/20191019200551 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /var/www/rails-new/releases/20191019200551 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/public /var/www/rails-new/releases/20191019200551/tmp cd /var/www/rails-new/releases/20191019200551 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /var/www/rails-new/releases/20191019200551/log ln -sf /var/www/rails-new/shared/node_modules /var/www/rails-new/releases/20191019200551/node_modules ln -sf /var/www/rails-new/shared/public/assets /var/www/rails-new/releases/20191019200551/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /var/www/rails-new/releases/20191019200551/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /var/www/rails-new/releases/20191019200551/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/tmp/sockets \u2022 core:write_release_json Writing 299 bytes to /var/www/rails-new/releases/20191019200551/.tomo_release.json \u2022 bundler:install cd /var/www/rails-new/releases/20191019200551 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The Gemfile's dependencies are satisfied \u2022 rails:db_migrate cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:migrate \u2022 rails:db_seed cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:seed \u2022 rails:assets_precompile cd /var/www/rails-new/releases/20191019200551 && bundle exec rails assets:precompile yarn install v1.16.0 [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@1.2.9: The platform \"linux\" is incompatible with this module. info \"fsevents@1.2.9\" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... warning \" > webpack-dev-server@3.8.1\" has unmet peer dependency \"webpack@^4.0.0\". warning \"webpack-dev-server > webpack-dev-middleware@3.7.2\" has unmet peer dependency \"webpack@^4.0.0\". [4/4] Building fresh packages... Done in 30.68s. I, [2019-10-19T20:06:30.645395 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css I, [2019-10-19T20:06:30.645832 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz Compiling\u2026 Compiled all packs in /var/www/rails-new/releases/20191019200551/public/packs WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option. You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands: npm install --save core-js@2 npm install --save core-js@3 yarn add core-js@2 yarn add core-js@3 \u2022 core:symlink_current ln -sf /var/www/rails-new/releases/20191019200551 /var/www/rails-new/current-0d3d2f7a6648294a mv -fT /var/www/rails-new/current-0d3d2f7a6648294a /var/www/rails-new/current \u2022 puma:restart systemctl --user start puma_rails-new.socket systemctl --user restart puma_rails-new.service \u2022 puma:check_active Checking if puma is active and listening on port 3000... systemctl --user is-active puma_rails-new.service curl -sS --connect-timeout 1 --max-time 10 http://localhost:3000 > /dev/null systemctl --user status puma_rails-new.service \u25cf puma_rails-new.service Loaded: loaded (enabled; vendor preset: enabled) Active: active (running) \u2022 core:clean_releases readlink /var/www/rails-new/current cd /var/www/rails-new/releases && ls -A1 \u2022 bundler:clean cd /var/www/rails-new/releases/20191019200551 && bundle clean The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. \u2022 core:log_revision Writing 100 bytes to /var/www/rails-new/revisions.log \u2714 Deployed rails-new to deployer@localhost:32829","title":"deploy"},{"location":"commands/deploy/#deploy","text":"Deploy the current project to remote host(s).","title":"deploy"},{"location":"commands/deploy/#usage","text":"$ tomo deploy [--dry-run] [options] Sequentially run the deploy list of tasks specified in .tomo/config.rb to deploy the project to a remote host. In practice, a deploy will usually consist of the following steps: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) During a deploy, tomo will initialize the :release_path setting based on the current date and time (e.g. /var/www/my-app/releases/20190616214752 ). Any tasks that copy files into a release ( git:create_release ), or run inside the release ( bundler:install , rails:assets_precompile , rails:db_migrate , etc.) will operate using this path. As a result, every tomo deploy will create a new entry in the releases directory, and the current symlink will point to the release that is currently active, i.e. the most recent successful deploy. The directory structure on the remote host looks like this: /var/www/my-app \u251c\u2500\u2500 git_repo/ \u251c\u2500\u2500 releases/ | \u251c\u2500\u2500 20190614192115/ | \u251c\u2500\u2500 20190615034736/ | \u2514\u2500\u2500 20190616214752/ \u251c\u2500\u2500 shared/ | \u251c\u2500\u2500 bundle/ | \u251c\u2500\u2500 log/ | \u251c\u2500\u2500 node_modules/ | \u2514\u2500\u2500 public/ | \u2514\u2500\u2500 assets/ \u251c\u2500\u2500 current -> /var/www/my-app/releases/20190616214752 \u2514\u2500\u2500 revisions.log This structure is customizable; see the core plugin settings for details. Tip: run tomo deploy --debug --dry-run to see an in-depth explanation of the settings and execution plan that will be used for the deployment.","title":"Usage"},{"location":"commands/deploy/#options","text":"Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/deploy/#example","text":"Given the following configuration: host \"deployer@localhost\", port: 32809 deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end Then a deploy would produce: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:update \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /var/www/rails-new/releases/20191019200551 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /var/www/rails-new/releases/20191019200551 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/public /var/www/rails-new/releases/20191019200551/tmp cd /var/www/rails-new/releases/20191019200551 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /var/www/rails-new/releases/20191019200551/log ln -sf /var/www/rails-new/shared/node_modules /var/www/rails-new/releases/20191019200551/node_modules ln -sf /var/www/rails-new/shared/public/assets /var/www/rails-new/releases/20191019200551/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /var/www/rails-new/releases/20191019200551/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /var/www/rails-new/releases/20191019200551/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/tmp/sockets \u2022 core:write_release_json Writing 299 bytes to /var/www/rails-new/releases/20191019200551/.tomo_release.json \u2022 bundler:install cd /var/www/rails-new/releases/20191019200551 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The Gemfile's dependencies are satisfied \u2022 rails:db_migrate cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:migrate \u2022 rails:db_seed cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:seed \u2022 rails:assets_precompile cd /var/www/rails-new/releases/20191019200551 && bundle exec rails assets:precompile yarn install v1.16.0 [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@1.2.9: The platform \"linux\" is incompatible with this module. info \"fsevents@1.2.9\" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... warning \" > webpack-dev-server@3.8.1\" has unmet peer dependency \"webpack@^4.0.0\". warning \"webpack-dev-server > webpack-dev-middleware@3.7.2\" has unmet peer dependency \"webpack@^4.0.0\". [4/4] Building fresh packages... Done in 30.68s. I, [2019-10-19T20:06:30.645395 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css I, [2019-10-19T20:06:30.645832 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz Compiling\u2026 Compiled all packs in /var/www/rails-new/releases/20191019200551/public/packs WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option. You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands: npm install --save core-js@2 npm install --save core-js@3 yarn add core-js@2 yarn add core-js@3 \u2022 core:symlink_current ln -sf /var/www/rails-new/releases/20191019200551 /var/www/rails-new/current-0d3d2f7a6648294a mv -fT /var/www/rails-new/current-0d3d2f7a6648294a /var/www/rails-new/current \u2022 puma:restart systemctl --user start puma_rails-new.socket systemctl --user restart puma_rails-new.service \u2022 puma:check_active Checking if puma is active and listening on port 3000... systemctl --user is-active puma_rails-new.service curl -sS --connect-timeout 1 --max-time 10 http://localhost:3000 > /dev/null systemctl --user status puma_rails-new.service \u25cf puma_rails-new.service Loaded: loaded (enabled; vendor preset: enabled) Active: active (running) \u2022 core:clean_releases readlink /var/www/rails-new/current cd /var/www/rails-new/releases && ls -A1 \u2022 bundler:clean cd /var/www/rails-new/releases/20191019200551 && bundle clean The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. \u2022 core:log_revision Writing 100 bytes to /var/www/rails-new/revisions.log \u2714 Deployed rails-new to deployer@localhost:32829","title":"Example"},{"location":"commands/init/","text":"init Start a new tomo project with a sample config. Usage $ tomo init [APP] Set up a new tomo project named APP . If APP is not specified, the name of the current directory will be used. This command creates a .tomo/config.rb file relative the current directory containing some example configuration. Refer to Configuration for a detailed explanation of this file. tomo init will make educated guesses about your project and fill in some configuration settings for you: nodenv_node_version based on node --version nodenv_install_yarn based on whether yarn is present git_url based on metadata in .git/ for this project, if present rbenv_ruby_version based on the version of Ruby being used to run tomo Options Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example $ cd my-rails-app $ tomo init \u2714 Created .tomo/config.rb","title":"init"},{"location":"commands/init/#init","text":"Start a new tomo project with a sample config.","title":"init"},{"location":"commands/init/#usage","text":"$ tomo init [APP] Set up a new tomo project named APP . If APP is not specified, the name of the current directory will be used. This command creates a .tomo/config.rb file relative the current directory containing some example configuration. Refer to Configuration for a detailed explanation of this file. tomo init will make educated guesses about your project and fill in some configuration settings for you: nodenv_node_version based on node --version nodenv_install_yarn based on whether yarn is present git_url based on metadata in .git/ for this project, if present rbenv_ruby_version based on the version of Ruby being used to run tomo","title":"Usage"},{"location":"commands/init/#options","text":"Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/init/#example","text":"$ cd my-rails-app $ tomo init \u2714 Created .tomo/config.rb","title":"Example"},{"location":"commands/run/","text":"run Run a specific remote task from the current project. Usage $ tomo run [--dry-run] [options] [--] TASK [ARGS...] Remotely run one specified TASK, optionally passing ARGS to that task. For example, if this project uses the rails plugin , you could run: $ tomo run -- rails:console --sandbox This will run the rails:console task on the host specified in .tomo/config.rb configuration file , and will pass the --sandbox argument to that task. The -- is used to separate tomo options from options that are passed to the task. If a task does not accept options, the -- can be omitted, like this: $ tomo run core:clean_releases When you specify a task name, the run command is implied and can be omitted, so this works as well: $ tomo core:clean_releases You can run any task defined by plugins loaded by the plugin declarations in .tomo/config.rb . To see a list of available tasks, run the tasks command. During the run command, tomo will initialize the :release_path setting to be equal to the current symlink (i.e. /var/www/my-app/current ). This means that the task will run within the current release. Options Option Purpose --[no-]privileged Run the task using a privileged user (e.g. root). This user is configured per host . -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example Given the following configuration: plugin \"puma\" host \"deployer@app.example.com\" Then we could run puma:restart like this: $ tomo run puma:restart tomo run v1.1.2 \u2192 Connecting to deployer@app.example.com \u2022 puma:restart systemctl --user start puma_example.socket systemctl --user restart puma_example.service \u2714 Ran puma:restart on deployer@app.example.com","title":"run"},{"location":"commands/run/#run","text":"Run a specific remote task from the current project.","title":"run"},{"location":"commands/run/#usage","text":"$ tomo run [--dry-run] [options] [--] TASK [ARGS...] Remotely run one specified TASK, optionally passing ARGS to that task. For example, if this project uses the rails plugin , you could run: $ tomo run -- rails:console --sandbox This will run the rails:console task on the host specified in .tomo/config.rb configuration file , and will pass the --sandbox argument to that task. The -- is used to separate tomo options from options that are passed to the task. If a task does not accept options, the -- can be omitted, like this: $ tomo run core:clean_releases When you specify a task name, the run command is implied and can be omitted, so this works as well: $ tomo core:clean_releases You can run any task defined by plugins loaded by the plugin declarations in .tomo/config.rb . To see a list of available tasks, run the tasks command. During the run command, tomo will initialize the :release_path setting to be equal to the current symlink (i.e. /var/www/my-app/current ). This means that the task will run within the current release.","title":"Usage"},{"location":"commands/run/#options","text":"Option Purpose --[no-]privileged Run the task using a privileged user (e.g. root). This user is configured per host . -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/run/#example","text":"Given the following configuration: plugin \"puma\" host \"deployer@app.example.com\" Then we could run puma:restart like this: $ tomo run puma:restart tomo run v1.1.2 \u2192 Connecting to deployer@app.example.com \u2022 puma:restart systemctl --user start puma_example.socket systemctl --user restart puma_example.service \u2714 Ran puma:restart on deployer@app.example.com","title":"Example"},{"location":"commands/setup/","text":"setup Prepare the current project for its first deploy. Usage $ tomo setup [--dry-run] [options] Prepare the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. During setup, tomo will initialize the :release_path setting to be a temporary directory based on the current date and time (e.g. /tmp/tomo-a4DBHX0P/20190616214752 ). This means setup tasks (e.g. rails:db_create , rails:db_schema_load ) run in a location that won\u2019t be deployed as an actual release. Options Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example Given the following configuration: host \"deployer@localhost\", port: 32829 setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Then a setup would produce: $ tomo setup tomo setup v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:setup Writing 314 bytes to /var/www/rails-new/envrc cat .bashrc Writing 3845 bytes to .bashrc \u2022 core:setup_directories mkdir -p /var/www/rails-new /var/www/rails-new/releases /var/www/rails-new/shared \u2022 git:clone [ -d /var/www/rails-new/git_repo ] mkdir -p /var/www/rails-new export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git clone --mirror git@github.com:mattbrictson/rails-new.git /var/www/rails-new/git_repo Cloning into bare repository '/var/www/rails-new/git_repo'... Warning: Permanently added 'github.com,192.30.255.113' (RSA) to the list of known hosts. \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /tmp/tomo-a4DBHX0P/20191019200138 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /tmp/tomo-a4DBHX0P/20191019200138 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/public /tmp/tomo-a4DBHX0P/20191019200138/tmp cd /tmp/tomo-a4DBHX0P/20191019200138 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /tmp/tomo-a4DBHX0P/20191019200138/log ln -sf /var/www/rails-new/shared/node_modules /tmp/tomo-a4DBHX0P/20191019200138/node_modules ln -sf /var/www/rails-new/shared/public/assets /tmp/tomo-a4DBHX0P/20191019200138/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /tmp/tomo-a4DBHX0P/20191019200138/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /tmp/tomo-a4DBHX0P/20191019200138/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/tmp/sockets \u2022 nodenv:install export PATH=$HOME/.nodenv/bin:$HOME/.nodenv/shims:$PATH && curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-installer | bash Installing nodenv with git... Initialized empty Git repository in /home/deployer/.nodenv/.git/ Updating origin From https://github.com/nodenv/nodenv * [new branch] master -> origin/master * [new tag] 0.2.0 -> 0.2.0 * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 * [new tag] v1.2.0 -> v1.2.0 * [new tag] v1.3.0 -> v1.3.0 Branch 'master' set up to track remote branch 'master' from 'origin'. Already on 'master' make: Entering directory '/home/deployer/.nodenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/nodenv-realpath.dylib -o ../libexec/nodenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.nodenv/src' Installing node-build with git... Cloning into '/home/deployer/.nodenv/plugins/node-build'... Running doctor script to verify installation... Checking for `nodenv' in PATH: /home/deployer/.nodenv/bin/nodenv Checking for nodenv shims in PATH: OK Checking `nodenv install' support: /home/deployer/.nodenv/plugins/node-build/bin/nodenv-install (node-build 4.6.4-9-g3a5ae01b) Counting installed Node versions: none There aren't any Node versions installed under `/home/deployer/.nodenv/versions'. You can install Node versions like so: nodenv install 2.2.4 Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.nodenv/bin' is added to PATH. 2. Run `nodenv init' to see instructions how to configure nodenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 3944 bytes to .bashrc nodenv versions nodenv install 10.16.0 Downloading node-v10.16.0-linux-x64.tar.gz... -> https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.gz Installing node-v10.16.0-linux-x64... Installed node-v10.16.0-linux-x64 to /home/deployer/.nodenv/versions/10.16.0 nodenv global 10.16.0 npm i -g yarn@1.16.0 /home/deployer/.nodenv/versions/10.16.0/bin/yarnpkg -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js /home/deployer/.nodenv/versions/10.16.0/bin/yarn -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js + yarn@1.16.0 added 1 package in 0.54s \u2022 rbenv:install export PATH=$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH && curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash Installing rbenv with git... Initialized empty Git repository in /home/deployer/.rbenv/.git/ Updating origin From https://github.com/rbenv/rbenv * [new branch] master -> origin/master * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.1.1 -> v0.1.1 * [new tag] v0.1.2 -> v0.1.2 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.2.1 -> v0.2.1 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 Already on 'master' Branch 'master' set up to track remote branch 'master' from 'origin'. make: Entering directory '/home/deployer/.rbenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/rbenv-realpath.dylib -o ../libexec/rbenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.rbenv/src' Installing ruby-build with git... Cloning into '/home/deployer/.rbenv/plugins/ruby-build'... Running doctor script to verify installation... Checking for `rbenv' in PATH: /home/deployer/.rbenv/bin/rbenv Checking for rbenv shims in PATH: OK Checking `rbenv install' support: /home/deployer/.rbenv/plugins/ruby-build/bin/rbenv-install (ruby-build 20191004) Counting installed Ruby versions: none There aren't any Ruby versions installed under `/home/deployer/.rbenv/versions'. You can install Ruby versions like so: rbenv install 2.2.4 Checking RubyGems settings: OK Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.rbenv/bin' is added to PATH. 2. Run `rbenv init' to see instructions how to configure rbenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 4041 bytes to .bashrc rbenv versions Installing ruby 2.6.5 -- this may take several minutes CFLAGS=-O3 rbenv install 2.6.5 Downloading ruby-2.6.5.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.5.tar.bz2 Installing ruby-2.6.5... Installed ruby-2.6.5 to /home/deployer/.rbenv/versions/2.6.5 rbenv global 2.6.5 \u2022 bundler:upgrade_bundler tail -n 10 /tmp/tomo-a4DBHX0P/20191019200138/Gemfile.lock gem install bundler --conservative --no-document -v 2.0.2 Successfully installed bundler-2.0.2 1 gem installed \u2022 bundler:config mkdir -p .bundle Writing 146 bytes to .bundle/config \u2022 bundler:install cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The following gems are missing * rake (13.0.0) * concurrent-ruby (1.1.5) * i18n (1.7.0) * minitest (5.12.2) * thread_safe (0.3.6) * tzinfo (1.2.5) * zeitwerk (2.2.0) * activesupport (6.0.0) * builder (3.2.3) * erubi (1.9.0) * mini_portile2 (2.4.0) * nokogiri (1.10.4) * rails-dom-testing (2.0.3) * crass (1.0.4) * loofah (2.3.0) * rails-html-sanitizer (1.3.0) * actionview (6.0.0) * rack (2.0.7) * rack-test (1.1.0) * actionpack (6.0.0) * nio4r (2.5.2) * websocket-extensions (0.1.4) * websocket-driver (0.7.1) * actioncable (6.0.0) * globalid (0.4.2) * activejob (6.0.0) * activemodel (6.0.0) * activerecord (6.0.0) * mimemagic (0.3.3) * marcel (0.3.3) * activestorage (6.0.0) * mini_mime (1.0.2) * mail (2.7.1) * actionmailbox (6.0.0) * actionmailer (6.0.0) * actiontext (6.0.0) * msgpack (1.3.1) * bootsnap (1.4.5) * ffi (1.11.1) * jbuilder (2.9.1) * method_source (0.9.2) * puma (3.12.1) * rack-proxy (0.6.5) * thor (0.20.3) * railties (6.0.0) * sprockets (3.7.2) * sprockets-rails (3.2.1) * rails (6.0.0) * rb-fsevent (0.10.3) * rb-inotify (0.10.0) * sass-listen (4.0.0) * sass (3.7.4) * tilt (2.0.10) * sass-rails (5.1.0) * sqlite3 (1.4.1) * turbolinks-source (5.2.0) * turbolinks (5.2.1) * webpacker (4.0.7) Install missing gems with `bundle install` cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle install The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching rake 13.0.0 Installing rake 13.0.0 Fetching thread_safe 0.3.6 Fetching concurrent-ruby 1.1.5 Fetching minitest 5.12.2 Installing minitest 5.12.2 Installing thread_safe 0.3.6 Installing concurrent-ruby 1.1.5 Fetching zeitwerk 2.2.0 Installing zeitwerk 2.2.0 Fetching builder 3.2.3 Fetching erubi 1.9.0 Installing builder 3.2.3 Installing erubi 1.9.0 Fetching mini_portile2 2.4.0 Installing mini_portile2 2.4.0 Fetching crass 1.0.4 Installing crass 1.0.4 Fetching rack 2.0.7 Installing rack 2.0.7 Fetching nio4r 2.5.2 Installing nio4r 2.5.2 with native extensions Fetching websocket-extensions 0.1.4 Installing websocket-extensions 0.1.4 Fetching mimemagic 0.3.3 Installing mimemagic 0.3.3 Fetching mini_mime 1.0.2 Fetching msgpack 1.3.1 Installing mini_mime 1.0.2 Using bundler 2.0.2 Fetching ffi 1.11.1 Installing msgpack 1.3.1 with native extensions Installing ffi 1.11.1 with native extensions Fetching method_source 0.9.2 Installing method_source 0.9.2 Fetching puma 3.12.1 Installing puma 3.12.1 with native extensions Fetching thor 0.20.3 Installing thor 0.20.3 Fetching rb-fsevent 0.10.3 Installing rb-fsevent 0.10.3 Fetching tilt 2.0.10 Installing tilt 2.0.10 Fetching sqlite3 1.4.1 Installing sqlite3 1.4.1 with native extensions Fetching turbolinks-source 5.2.0 Installing turbolinks-source 5.2.0 Fetching tzinfo 1.2.5 Installing tzinfo 1.2.5 Fetching nokogiri 1.10.4 Installing nokogiri 1.10.4 with native extensions Fetching i18n 1.7.0 Installing i18n 1.7.0 Fetching websocket-driver 0.7.1 Installing websocket-driver 0.7.1 with native extensions Fetching marcel 0.3.3 Installing marcel 0.3.3 Fetching rack-test 1.1.0 Installing rack-test 1.1.0 Fetching rack-proxy 0.6.5 Installing rack-proxy 0.6.5 Fetching sprockets 3.7.2 Installing sprockets 3.7.2 Fetching mail 2.7.1 Installing mail 2.7.1 Fetching bootsnap 1.4.5 Installing bootsnap 1.4.5 with native extensions Fetching rb-inotify 0.10.0 Installing rb-inotify 0.10.0 Fetching turbolinks 5.2.1 Installing turbolinks 5.2.1 Fetching activesupport 6.0.0 Installing activesupport 6.0.0 Fetching loofah 2.3.0 Installing loofah 2.3.0 Fetching sass-listen 4.0.0 Installing sass-listen 4.0.0 Fetching rails-html-sanitizer 1.3.0 Fetching sass 3.7.4 Fetching rails-dom-testing 2.0.3 Installing rails-html-sanitizer 1.3.0 Fetching globalid 0.4.2 Installing rails-dom-testing 2.0.3 Installing globalid 0.4.2 Installing sass 3.7.4 Fetching activemodel 6.0.0 Fetching jbuilder 2.9.1 Installing jbuilder 2.9.1 Installing activemodel 6.0.0 Fetching activejob 6.0.0 Installing activejob 6.0.0 Fetching actionview 6.0.0 Installing actionview 6.0.0 Fetching activerecord 6.0.0 Installing activerecord 6.0.0 Fetching actionpack 6.0.0 Installing actionpack 6.0.0 Fetching actioncable 6.0.0 Fetching actionmailer 6.0.0 Fetching railties 6.0.0 Installing actionmailer 6.0.0 Installing actioncable 6.0.0 Fetching sprockets-rails 3.2.1 Installing railties 6.0.0 Installing sprockets-rails 3.2.1 Fetching activestorage 6.0.0 Installing activestorage 6.0.0 Fetching actionmailbox 6.0.0 Fetching actiontext 6.0.0 Installing actionmailbox 6.0.0 Installing actiontext 6.0.0 Fetching sass-rails 5.1.0 Fetching rails 6.0.0 Fetching webpacker 4.0.7 Installing sass-rails 5.1.0 Installing rails 6.0.0 Installing webpacker 4.0.7 Bundle complete! 17 Gemfile dependencies, 59 gems now installed. Gems in the groups development and test were not installed. Bundled gems are installed into `/var/www/rails-new/shared/bundle` Post-install message from i18n: HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application. Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x. For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0 Post-install message from sass: Ruby Sass has reached end-of-life and should no longer be used. * If you use Sass as a command-line tool, we recommend using Dart Sass, the new primary implementation: https://sass-lang.com/install * If you use Sass as a plug-in for a Ruby web framework, we recommend using the sassc gem: https://github.com/sass/sassc-ruby#readme * For more details, please refer to the Sass blog: https://sass-lang.com/blog/posts/7828841 \u2022 rails:db_create cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:version Database already exists; skipping db:create. \u2022 rails:db_schema_load [ -f /tmp/tomo-a4DBHX0P/20191019200138/db/schema.rb ] WARNING: db/schema.rb is not present; skipping schema:load. \u2022 rails:db_seed cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:seed \u2022 puma:setup_systemd loginctl user-status deployer mkdir -p .config/systemd/user Writing 218 bytes to .config/systemd/user/puma_rails-new.socket Writing 570 bytes to .config/systemd/user/puma_rails-new.service systemctl --user daemon-reload systemctl --user enable puma_rails-new.service puma_rails-new.socket \u2714 Performed setup of rails-new on deployer@localhost:32829","title":"setup"},{"location":"commands/setup/#setup","text":"Prepare the current project for its first deploy.","title":"setup"},{"location":"commands/setup/#usage","text":"$ tomo setup [--dry-run] [options] Prepare the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. During setup, tomo will initialize the :release_path setting to be a temporary directory based on the current date and time (e.g. /tmp/tomo-a4DBHX0P/20190616214752 ). This means setup tasks (e.g. rails:db_create , rails:db_schema_load ) run in a location that won\u2019t be deployed as an actual release.","title":"Usage"},{"location":"commands/setup/#options","text":"Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/setup/#example","text":"Given the following configuration: host \"deployer@localhost\", port: 32829 setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Then a setup would produce: $ tomo setup tomo setup v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:setup Writing 314 bytes to /var/www/rails-new/envrc cat .bashrc Writing 3845 bytes to .bashrc \u2022 core:setup_directories mkdir -p /var/www/rails-new /var/www/rails-new/releases /var/www/rails-new/shared \u2022 git:clone [ -d /var/www/rails-new/git_repo ] mkdir -p /var/www/rails-new export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git clone --mirror git@github.com:mattbrictson/rails-new.git /var/www/rails-new/git_repo Cloning into bare repository '/var/www/rails-new/git_repo'... Warning: Permanently added 'github.com,192.30.255.113' (RSA) to the list of known hosts. \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /tmp/tomo-a4DBHX0P/20191019200138 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /tmp/tomo-a4DBHX0P/20191019200138 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/public /tmp/tomo-a4DBHX0P/20191019200138/tmp cd /tmp/tomo-a4DBHX0P/20191019200138 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /tmp/tomo-a4DBHX0P/20191019200138/log ln -sf /var/www/rails-new/shared/node_modules /tmp/tomo-a4DBHX0P/20191019200138/node_modules ln -sf /var/www/rails-new/shared/public/assets /tmp/tomo-a4DBHX0P/20191019200138/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /tmp/tomo-a4DBHX0P/20191019200138/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /tmp/tomo-a4DBHX0P/20191019200138/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/tmp/sockets \u2022 nodenv:install export PATH=$HOME/.nodenv/bin:$HOME/.nodenv/shims:$PATH && curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-installer | bash Installing nodenv with git... Initialized empty Git repository in /home/deployer/.nodenv/.git/ Updating origin From https://github.com/nodenv/nodenv * [new branch] master -> origin/master * [new tag] 0.2.0 -> 0.2.0 * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 * [new tag] v1.2.0 -> v1.2.0 * [new tag] v1.3.0 -> v1.3.0 Branch 'master' set up to track remote branch 'master' from 'origin'. Already on 'master' make: Entering directory '/home/deployer/.nodenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/nodenv-realpath.dylib -o ../libexec/nodenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.nodenv/src' Installing node-build with git... Cloning into '/home/deployer/.nodenv/plugins/node-build'... Running doctor script to verify installation... Checking for `nodenv' in PATH: /home/deployer/.nodenv/bin/nodenv Checking for nodenv shims in PATH: OK Checking `nodenv install' support: /home/deployer/.nodenv/plugins/node-build/bin/nodenv-install (node-build 4.6.4-9-g3a5ae01b) Counting installed Node versions: none There aren't any Node versions installed under `/home/deployer/.nodenv/versions'. You can install Node versions like so: nodenv install 2.2.4 Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.nodenv/bin' is added to PATH. 2. Run `nodenv init' to see instructions how to configure nodenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 3944 bytes to .bashrc nodenv versions nodenv install 10.16.0 Downloading node-v10.16.0-linux-x64.tar.gz... -> https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.gz Installing node-v10.16.0-linux-x64... Installed node-v10.16.0-linux-x64 to /home/deployer/.nodenv/versions/10.16.0 nodenv global 10.16.0 npm i -g yarn@1.16.0 /home/deployer/.nodenv/versions/10.16.0/bin/yarnpkg -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js /home/deployer/.nodenv/versions/10.16.0/bin/yarn -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js + yarn@1.16.0 added 1 package in 0.54s \u2022 rbenv:install export PATH=$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH && curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash Installing rbenv with git... Initialized empty Git repository in /home/deployer/.rbenv/.git/ Updating origin From https://github.com/rbenv/rbenv * [new branch] master -> origin/master * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.1.1 -> v0.1.1 * [new tag] v0.1.2 -> v0.1.2 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.2.1 -> v0.2.1 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 Already on 'master' Branch 'master' set up to track remote branch 'master' from 'origin'. make: Entering directory '/home/deployer/.rbenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/rbenv-realpath.dylib -o ../libexec/rbenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.rbenv/src' Installing ruby-build with git... Cloning into '/home/deployer/.rbenv/plugins/ruby-build'... Running doctor script to verify installation... Checking for `rbenv' in PATH: /home/deployer/.rbenv/bin/rbenv Checking for rbenv shims in PATH: OK Checking `rbenv install' support: /home/deployer/.rbenv/plugins/ruby-build/bin/rbenv-install (ruby-build 20191004) Counting installed Ruby versions: none There aren't any Ruby versions installed under `/home/deployer/.rbenv/versions'. You can install Ruby versions like so: rbenv install 2.2.4 Checking RubyGems settings: OK Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.rbenv/bin' is added to PATH. 2. Run `rbenv init' to see instructions how to configure rbenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 4041 bytes to .bashrc rbenv versions Installing ruby 2.6.5 -- this may take several minutes CFLAGS=-O3 rbenv install 2.6.5 Downloading ruby-2.6.5.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.5.tar.bz2 Installing ruby-2.6.5... Installed ruby-2.6.5 to /home/deployer/.rbenv/versions/2.6.5 rbenv global 2.6.5 \u2022 bundler:upgrade_bundler tail -n 10 /tmp/tomo-a4DBHX0P/20191019200138/Gemfile.lock gem install bundler --conservative --no-document -v 2.0.2 Successfully installed bundler-2.0.2 1 gem installed \u2022 bundler:config mkdir -p .bundle Writing 146 bytes to .bundle/config \u2022 bundler:install cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The following gems are missing * rake (13.0.0) * concurrent-ruby (1.1.5) * i18n (1.7.0) * minitest (5.12.2) * thread_safe (0.3.6) * tzinfo (1.2.5) * zeitwerk (2.2.0) * activesupport (6.0.0) * builder (3.2.3) * erubi (1.9.0) * mini_portile2 (2.4.0) * nokogiri (1.10.4) * rails-dom-testing (2.0.3) * crass (1.0.4) * loofah (2.3.0) * rails-html-sanitizer (1.3.0) * actionview (6.0.0) * rack (2.0.7) * rack-test (1.1.0) * actionpack (6.0.0) * nio4r (2.5.2) * websocket-extensions (0.1.4) * websocket-driver (0.7.1) * actioncable (6.0.0) * globalid (0.4.2) * activejob (6.0.0) * activemodel (6.0.0) * activerecord (6.0.0) * mimemagic (0.3.3) * marcel (0.3.3) * activestorage (6.0.0) * mini_mime (1.0.2) * mail (2.7.1) * actionmailbox (6.0.0) * actionmailer (6.0.0) * actiontext (6.0.0) * msgpack (1.3.1) * bootsnap (1.4.5) * ffi (1.11.1) * jbuilder (2.9.1) * method_source (0.9.2) * puma (3.12.1) * rack-proxy (0.6.5) * thor (0.20.3) * railties (6.0.0) * sprockets (3.7.2) * sprockets-rails (3.2.1) * rails (6.0.0) * rb-fsevent (0.10.3) * rb-inotify (0.10.0) * sass-listen (4.0.0) * sass (3.7.4) * tilt (2.0.10) * sass-rails (5.1.0) * sqlite3 (1.4.1) * turbolinks-source (5.2.0) * turbolinks (5.2.1) * webpacker (4.0.7) Install missing gems with `bundle install` cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle install The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching rake 13.0.0 Installing rake 13.0.0 Fetching thread_safe 0.3.6 Fetching concurrent-ruby 1.1.5 Fetching minitest 5.12.2 Installing minitest 5.12.2 Installing thread_safe 0.3.6 Installing concurrent-ruby 1.1.5 Fetching zeitwerk 2.2.0 Installing zeitwerk 2.2.0 Fetching builder 3.2.3 Fetching erubi 1.9.0 Installing builder 3.2.3 Installing erubi 1.9.0 Fetching mini_portile2 2.4.0 Installing mini_portile2 2.4.0 Fetching crass 1.0.4 Installing crass 1.0.4 Fetching rack 2.0.7 Installing rack 2.0.7 Fetching nio4r 2.5.2 Installing nio4r 2.5.2 with native extensions Fetching websocket-extensions 0.1.4 Installing websocket-extensions 0.1.4 Fetching mimemagic 0.3.3 Installing mimemagic 0.3.3 Fetching mini_mime 1.0.2 Fetching msgpack 1.3.1 Installing mini_mime 1.0.2 Using bundler 2.0.2 Fetching ffi 1.11.1 Installing msgpack 1.3.1 with native extensions Installing ffi 1.11.1 with native extensions Fetching method_source 0.9.2 Installing method_source 0.9.2 Fetching puma 3.12.1 Installing puma 3.12.1 with native extensions Fetching thor 0.20.3 Installing thor 0.20.3 Fetching rb-fsevent 0.10.3 Installing rb-fsevent 0.10.3 Fetching tilt 2.0.10 Installing tilt 2.0.10 Fetching sqlite3 1.4.1 Installing sqlite3 1.4.1 with native extensions Fetching turbolinks-source 5.2.0 Installing turbolinks-source 5.2.0 Fetching tzinfo 1.2.5 Installing tzinfo 1.2.5 Fetching nokogiri 1.10.4 Installing nokogiri 1.10.4 with native extensions Fetching i18n 1.7.0 Installing i18n 1.7.0 Fetching websocket-driver 0.7.1 Installing websocket-driver 0.7.1 with native extensions Fetching marcel 0.3.3 Installing marcel 0.3.3 Fetching rack-test 1.1.0 Installing rack-test 1.1.0 Fetching rack-proxy 0.6.5 Installing rack-proxy 0.6.5 Fetching sprockets 3.7.2 Installing sprockets 3.7.2 Fetching mail 2.7.1 Installing mail 2.7.1 Fetching bootsnap 1.4.5 Installing bootsnap 1.4.5 with native extensions Fetching rb-inotify 0.10.0 Installing rb-inotify 0.10.0 Fetching turbolinks 5.2.1 Installing turbolinks 5.2.1 Fetching activesupport 6.0.0 Installing activesupport 6.0.0 Fetching loofah 2.3.0 Installing loofah 2.3.0 Fetching sass-listen 4.0.0 Installing sass-listen 4.0.0 Fetching rails-html-sanitizer 1.3.0 Fetching sass 3.7.4 Fetching rails-dom-testing 2.0.3 Installing rails-html-sanitizer 1.3.0 Fetching globalid 0.4.2 Installing rails-dom-testing 2.0.3 Installing globalid 0.4.2 Installing sass 3.7.4 Fetching activemodel 6.0.0 Fetching jbuilder 2.9.1 Installing jbuilder 2.9.1 Installing activemodel 6.0.0 Fetching activejob 6.0.0 Installing activejob 6.0.0 Fetching actionview 6.0.0 Installing actionview 6.0.0 Fetching activerecord 6.0.0 Installing activerecord 6.0.0 Fetching actionpack 6.0.0 Installing actionpack 6.0.0 Fetching actioncable 6.0.0 Fetching actionmailer 6.0.0 Fetching railties 6.0.0 Installing actionmailer 6.0.0 Installing actioncable 6.0.0 Fetching sprockets-rails 3.2.1 Installing railties 6.0.0 Installing sprockets-rails 3.2.1 Fetching activestorage 6.0.0 Installing activestorage 6.0.0 Fetching actionmailbox 6.0.0 Fetching actiontext 6.0.0 Installing actionmailbox 6.0.0 Installing actiontext 6.0.0 Fetching sass-rails 5.1.0 Fetching rails 6.0.0 Fetching webpacker 4.0.7 Installing sass-rails 5.1.0 Installing rails 6.0.0 Installing webpacker 4.0.7 Bundle complete! 17 Gemfile dependencies, 59 gems now installed. Gems in the groups development and test were not installed. Bundled gems are installed into `/var/www/rails-new/shared/bundle` Post-install message from i18n: HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application. Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x. For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0 Post-install message from sass: Ruby Sass has reached end-of-life and should no longer be used. * If you use Sass as a command-line tool, we recommend using Dart Sass, the new primary implementation: https://sass-lang.com/install * If you use Sass as a plug-in for a Ruby web framework, we recommend using the sassc gem: https://github.com/sass/sassc-ruby#readme * For more details, please refer to the Sass blog: https://sass-lang.com/blog/posts/7828841 \u2022 rails:db_create cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:version Database already exists; skipping db:create. \u2022 rails:db_schema_load [ -f /tmp/tomo-a4DBHX0P/20191019200138/db/schema.rb ] WARNING: db/schema.rb is not present; skipping schema:load. \u2022 rails:db_seed cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:seed \u2022 puma:setup_systemd loginctl user-status deployer mkdir -p .config/systemd/user Writing 218 bytes to .config/systemd/user/puma_rails-new.socket Writing 570 bytes to .config/systemd/user/puma_rails-new.service systemctl --user daemon-reload systemctl --user enable puma_rails-new.service puma_rails-new.socket \u2714 Performed setup of rails-new on deployer@localhost:32829","title":"Example"},{"location":"commands/tasks/","text":"tasks List all tasks that can be used with the run command. Usage $ tomo tasks List all tomo tasks (i.e. those that can be used with tomo run ). Available tasks are those defined by plugins loaded in .tomo/config.rb . Refer to the Configuration guide for an explanation of how plugins are loaded. The reference documentation for each plugin (e.g. core , git ) describes the tasks these plugins provide. Options Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example $ tomo tasks bundler:clean bundler:install bundler:upgrade_bundler core:clean_releases core:log_revision core:setup_directories core:symlink_current core:symlink_shared core:write_release_json env:set env:setup env:show env:unset env:update git:clone git:create_release nodenv:install puma:restart rails:assets_precompile rails:console rails:db_create rails:db_migrate rails:db_schema_load rails:db_seed rails:db_setup rails:db_structure_load rbenv:install","title":"tasks"},{"location":"commands/tasks/#tasks","text":"List all tasks that can be used with the run command.","title":"tasks"},{"location":"commands/tasks/#usage","text":"$ tomo tasks List all tomo tasks (i.e. those that can be used with tomo run ). Available tasks are those defined by plugins loaded in .tomo/config.rb . Refer to the Configuration guide for an explanation of how plugins are loaded. The reference documentation for each plugin (e.g. core , git ) describes the tasks these plugins provide.","title":"Usage"},{"location":"commands/tasks/#options","text":"Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/tasks/#example","text":"$ tomo tasks bundler:clean bundler:install bundler:upgrade_bundler core:clean_releases core:log_revision core:setup_directories core:symlink_current core:symlink_shared core:write_release_json env:set env:setup env:show env:unset env:update git:clone git:create_release nodenv:install puma:restart rails:assets_precompile rails:console rails:db_create rails:db_migrate rails:db_schema_load rails:db_seed rails:db_setup rails:db_structure_load rbenv:install","title":"Example"},{"location":"plugins/bundler/","text":"bundler The bundler plugin installs ruby gem dependencies using bundler. This is required for deploying Rails apps. It also provides conveniences for using bundle exec . Settings Note that the settings listed here only take effect if you run the bundler:config task. Name Purpose Default bundler_config_path Location where the bundler:config task will write bundler\u2019s configuration file \".bundle/config\" bundler_deployment Enables bundler\u2019s deployment mode (strongly recommended) true bundler_gemfile Optionally used to override the location of the Gemfile nil bundler_ignore_messages Hide gem post-install messages during bundle install true bundler_jobs Override bundler\u2019s default (number of processors) amount of concurrency used when downloading/installing gems nil bundler_path Directory where gems where be installed \"%{shared_path}/bundle\" bundler_retry Number of times to retry installing a gem if it fails to download \"3\" bundler_version The version of bundler to install, used by the bundler:upgrade_bundler task; if nil (the default), determine the version based on Gemfile.lock nil bundler_without Array of Gemfile groups to exclude from installation [\"development\", \"test\"] Tasks bundler:upgrade_bundler Installs the version of bundler specified by the :bundler_version setting, if specified. If :bundler_version is nil (the default), this task will automatically determine the version of bundler required by the app that is being deployed by looking at the BUNDLED WITH entry within the app\u2019s Gemfile.lock . Bundler will be installed withing this command: gem install bundler --conservative --no-document -v VERSION bundler:upgrade_bundler is intended for use as a setup task. It should be run prior to bundler:install to ensure that the correct version bundler is present. bundler:config Writes a .bundle/config file with the configuration specified by the various :bundler_* tomo settings. This ensures that invocations of bundle check , bundle install , and most importantly bundle exec all consistently use the correct bundler configuration. bundler:config is intended for use as a setup task. It should be run prior to bundler:install so that gems are installed in the proper location. bundler:install Runs bundle install to download and install all the dependencies specified by the Gemfile of the app that is being deployed. As a performance optimization, this task will run bundle check first to see if the app\u2019s dependencies have already been installed. If so, bundle install is skipped. bundler:install is intended for use as a deploy task. It should be run prior to any tasks that rely on gems. bundler:clean Runs bundle clean to delete any previously installed gems that are no longer needed by the current version of the app. Cleaning is generally good practice to save disk space and speed up app launch time. bundler:clean is intended for use as a deploy task. It should be run at the conclusion of the deploy after all other tasks. Helpers These helper methods become available on instances of Remote when the bundler plugin is loaded. They accept the same options as Remote#run . remote.bundle(*args, **options) \u2192 Tomo::Result Runs bundle within release_path by default. remote.bundle(\"exec\", \"rails\", \"console\") # $ cd /var/www/my-app/current && bundle exec rails console remote.bundle?(*args, **options) \u2192 true or false Same as bundle but returns true if the command succeeded, false otherwise.","title":"bundler"},{"location":"plugins/bundler/#bundler","text":"The bundler plugin installs ruby gem dependencies using bundler. This is required for deploying Rails apps. It also provides conveniences for using bundle exec .","title":"bundler"},{"location":"plugins/bundler/#settings","text":"Note that the settings listed here only take effect if you run the bundler:config task. Name Purpose Default bundler_config_path Location where the bundler:config task will write bundler\u2019s configuration file \".bundle/config\" bundler_deployment Enables bundler\u2019s deployment mode (strongly recommended) true bundler_gemfile Optionally used to override the location of the Gemfile nil bundler_ignore_messages Hide gem post-install messages during bundle install true bundler_jobs Override bundler\u2019s default (number of processors) amount of concurrency used when downloading/installing gems nil bundler_path Directory where gems where be installed \"%{shared_path}/bundle\" bundler_retry Number of times to retry installing a gem if it fails to download \"3\" bundler_version The version of bundler to install, used by the bundler:upgrade_bundler task; if nil (the default), determine the version based on Gemfile.lock nil bundler_without Array of Gemfile groups to exclude from installation [\"development\", \"test\"]","title":"Settings"},{"location":"plugins/bundler/#tasks","text":"","title":"Tasks"},{"location":"plugins/bundler/#bundlerupgrade_bundler","text":"Installs the version of bundler specified by the :bundler_version setting, if specified. If :bundler_version is nil (the default), this task will automatically determine the version of bundler required by the app that is being deployed by looking at the BUNDLED WITH entry within the app\u2019s Gemfile.lock . Bundler will be installed withing this command: gem install bundler --conservative --no-document -v VERSION bundler:upgrade_bundler is intended for use as a setup task. It should be run prior to bundler:install to ensure that the correct version bundler is present.","title":"bundler:upgrade_bundler"},{"location":"plugins/bundler/#bundlerconfig","text":"Writes a .bundle/config file with the configuration specified by the various :bundler_* tomo settings. This ensures that invocations of bundle check , bundle install , and most importantly bundle exec all consistently use the correct bundler configuration. bundler:config is intended for use as a setup task. It should be run prior to bundler:install so that gems are installed in the proper location.","title":"bundler:config"},{"location":"plugins/bundler/#bundlerinstall","text":"Runs bundle install to download and install all the dependencies specified by the Gemfile of the app that is being deployed. As a performance optimization, this task will run bundle check first to see if the app\u2019s dependencies have already been installed. If so, bundle install is skipped. bundler:install is intended for use as a deploy task. It should be run prior to any tasks that rely on gems.","title":"bundler:install"},{"location":"plugins/bundler/#bundlerclean","text":"Runs bundle clean to delete any previously installed gems that are no longer needed by the current version of the app. Cleaning is generally good practice to save disk space and speed up app launch time. bundler:clean is intended for use as a deploy task. It should be run at the conclusion of the deploy after all other tasks.","title":"bundler:clean"},{"location":"plugins/bundler/#helpers","text":"These helper methods become available on instances of Remote when the bundler plugin is loaded. They accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/bundler/#remotebundleargs-options-tomoresult","text":"Runs bundle within release_path by default. remote.bundle(\"exec\", \"rails\", \"console\") # $ cd /var/www/my-app/current && bundle exec rails console","title":"remote.bundle(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/bundler/#remotebundleargs-options-true-or-false","text":"Same as bundle but returns true if the command succeeded, false otherwise.","title":"remote.bundle?(*args, **options) \u2192 true or false"},{"location":"plugins/core/","text":"core The core plugin provides tasks, settings, and helpers that are the fundamental building blocks for most tomo deployments. This plugin is always loaded and available, even if it is not explicitly declared in the configuration file. Settings Name Purpose Default application The name of the application being deployed \"default\" concurrency The maximum number of threads to use when deploying to multiple hosts at once 10 current_path Location of the symlink that points to the currently deployed release \"%{deploy_to}/current\" deploy_to The root directory under which all tomo data, releases, etc. are stored \"/var/www/%{application}\" keep_releases Number of releases to keep when pruning old releases with the core:clean_releases task 10 linked_dirs Array of directory names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] linked_files Array of file names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] local_user User name that will be written to the revision log and the release JSON file as the \u201cdeploy user\u201d. $USER , $USERNAME , or whoami releases_path Directory where the deploy command creates releases \"%{deploy_to}/releases\" release_json_path Path where the core:write_release_json task will write a JSON file describing the release \"%{release_path}/.tomo_release.json\" revision_log_path Path where the core:log_revision task will append a log message with the date and time of the release \"%{deploy_to}/revisions.log\" run_args A special read-only setting where tomo places any extra arguments that are passed to the run command [] shared_path Directory where files shared between releases are stored; used by core:symlink_shared \"%{deploy_to}/shared\" ssh_connect_timeout The number of seconds tomo will wait before it gives up when trying to open an SSH connection 5 ssh_executable The name (or full path) of the ssh executable \"ssh\" ssh_extra_opts An array of extra command line arguments that tomo will pass to every invocation of the ssh executable [\"-o\", \"PasswordAuthentication=no\"] ssh_forward_agent Whether to forward authentication when connecting via SSH; needed for seamless git+ssh true ssh_reuse_connections Whether to use ControlMaster to keep connections open across multiple invocations of ssh; setting this to false will slow down tomo significantly true ssh_strict_host_key_checking Use \"accept-new\" for a good compromise of security and convenience, true for most security, false for most convenience; note that older versions of ssh do not understand the \"accept-new\" option \"accept-new\" tmp_path Directory where the setup command stages temporary files \"/tmp/tomo-#{SecureRandom.alphanumeric(8)}\" tomo_config_file_path A special read-only setting containing the path to the config.rb file that was used to configure tomo \"/path/to/.tomo/config.rb\" Tasks core:setup_directories Creates the :deploy_to , :shared_path , and :releases_path directories so that other tasks that rely on these directories can work. This is one of the first tasks that should be run as part of setup . core:symlink_shared Creates a symlink for each directory listed in the :linked_dirs setting and each file in :linked_files . The symlink will point to the directory or file of the same name inside the shared directory. This allows these directories and files to be shared across all releases. Note that if a directory or file already exists in the release, that directory or file will be deleted or overwritten prior to creating the link. For example, given this configuration: set linked_dirs: [\"public/assets\"] set linked_files: [\"config/database.yml\"] Calling this task will run: mkdir -p /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public cd /var/www/my-app/releases/20190604204415 && rm -rf public/assets ln -sf /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public/assets ln -sfn /var/www/my-app/shared/config/database.yml \\ /var/www/my-app/releases/20190604204415/config/database.yml core:symlink_shared is intended for use as a deploy task. If :linked_dirs and :linked_files are both empty, running this task has no effect. core:symlink_current Promotes the release that is currently being deployed to become the \u201ccurrent\u201d release by updating the current symlink. core:symlink_shared is intended for use as a deploy task. It is typically run after all build steps have completed ( bundler:install , rails:db_migrate , rails:assets_precompile , etc.). core:clean_releases Deletes old releases while maintaining the most recent releases and keeping the current release. The total number of releases kept will be based on the :keep_releases setting. If this setting is absent or zero, running this task has no effect. If you are continuously deploying your application in an automated fashion, the releases can quickly fill up disk space if they are not pruned; hence the need for this task. core:clean_releases is intended for use as a deploy task. It is typically run at the end of a deployment once everything else has succeeded. core:write_release_json Writes a JSON file to the location specified by the :release_json_path setting. This file will contain a JSON object with properties that describe the release. Here is an example: { \"ref\": \"main\", \"author\": \"matt@example.com\", \"revision\": \"0d1cb3212e2f9c43aa49fb172d8d9c726163cecf\", \"revision_date\": \"2019-06-01 17:23:48 -0700\", \"deploy_date\": \"2019-06-05 19:00:26 -0700\", \"deploy_user\": \"mbrictson\" } core:log_revision Appends a message to a log file specified by the :revision_log_path setting. The message contains information about the release. Here is an example entry: 2019-06-05 19:00:26 -0700 - 0d1cb3212e2f9c43aa49fb172d8d9c726163cecf (main) deployed by mbrictson Helpers All of these methods are available on instances of Remote and accept the same options as Remote#run . remote.capture(*command, **options) \u2192 String Run the given command, returning the stdout of that command. If the command did not write to stdout, then return an empty String. Note that stderr is ignored, and an exception will be thrown if the command fails. remote.capture(\"echo\", \"hello\") # => \"hello\\n\" remote.run?(*command, **options) \u2192 true or false Run the given command, returning true if the command succeeded (exit status of 0) or false otherwise. # If java is not installed in the $PATH remote.run?(\"which\", \"java\") # => false remote.write(text:/template:, to:, append: false, **options) \u2192 Tomo::Result Write the given text (a String) or the text resulting from merging the given template (a local path to an ERB template file) to the remote path specified by to: . Refer to the merge_template documentation for details on tomo\u2019s ERB templating behavior. If append is false (the default), the remote file will completely replaced; if true , the file will be appended to. This is designed for small amounts of text (e.g. configuration files), not large or binary data. remote.write text: \"hello world!\\n\", to: paths.shared.join(\"greetings.txt\"), append: true remote.write template: File.expand_path(\"unicorn.service.erb\", __dir__), to: \".config/systemd/user/unicorn.service\" remote.ln_sf(target, link, **options) \u2192 Tomo::Result Create a symlink on the remote host at the path specified at link that points to target . Deletes any existing file that already exists at the link path prior to creating the symlink. remote.ln_sf(paths.shared.join(\".env\"), paths.release.join(\".env\")) # $ ln -sf /var/www/my-app/shared/.env /var/www/my-app/releases/20190604204415/.env remote.ln_sfn(target, link, **options) \u2192 Tomo::Result Like ln_sf but also passes the -n flag, which allows an existing link to be deleted even if it is a symlink to a directory. remote.mkdir_p(*directories, **options) \u2192 Tomo::Result Creates one or more directories on the remote host. remote.mkdir_p(paths.current.dirname, paths.shared) # $ mkdir -p /var/www/my-app /var/www/my-app/shared remote.rm_rf(*paths, **options) \u2192 Tomo::Result Deletes one or more files or directories on the remote host. remote.rm_rf(paths.tmp) # $ rm -rf /tmp/tomo-a4DBHX0P remote.list_files(directory=nil, **options) \u2192 [String] Lists non-hidden files in the specified directory. If directory is omitted, the default SSH login directory is used (typically the deploy user\u2019s home directory). The result will be an array of the directory contents. remote.list_files(\"/var/www/my-app\") # => [\"current\", \"releases\", \"revision.log\", shared\"] remote.command_available?(command_name, **options) \u2192 true or false Runs which on the remote host to determine whether the given command_name is an available executable. Returns true if an executable exists, false otherwise. remote.command_available?(\"java\") # => false remote.file?(file, **options) \u2192 true or false Uses the shell expression [ -f ] to test whether the given file exists on the remote host. Returns true if the path exists and is a normal file, false otherwise. remote.file?(\".bashrc\") # => true remote.executable?(file, **options) \u2192 true or false Uses the shell expression [ -x ] to test whether the given file exists on the remote host. Returns true if the path exists and is executable, false otherwise. remote.executable?(\"/usr/bin/git\") # => true remote.directory?(directory, **options) \u2192 true or false Uses the shell expression [ -d ] to test whether the given directory exists on the remote host. Returns true if the path exists and is a directory, false otherwise. remote.directory?(\"/opt\") # => true","title":"core"},{"location":"plugins/core/#core","text":"The core plugin provides tasks, settings, and helpers that are the fundamental building blocks for most tomo deployments. This plugin is always loaded and available, even if it is not explicitly declared in the configuration file.","title":"core"},{"location":"plugins/core/#settings","text":"Name Purpose Default application The name of the application being deployed \"default\" concurrency The maximum number of threads to use when deploying to multiple hosts at once 10 current_path Location of the symlink that points to the currently deployed release \"%{deploy_to}/current\" deploy_to The root directory under which all tomo data, releases, etc. are stored \"/var/www/%{application}\" keep_releases Number of releases to keep when pruning old releases with the core:clean_releases task 10 linked_dirs Array of directory names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] linked_files Array of file names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] local_user User name that will be written to the revision log and the release JSON file as the \u201cdeploy user\u201d. $USER , $USERNAME , or whoami releases_path Directory where the deploy command creates releases \"%{deploy_to}/releases\" release_json_path Path where the core:write_release_json task will write a JSON file describing the release \"%{release_path}/.tomo_release.json\" revision_log_path Path where the core:log_revision task will append a log message with the date and time of the release \"%{deploy_to}/revisions.log\" run_args A special read-only setting where tomo places any extra arguments that are passed to the run command [] shared_path Directory where files shared between releases are stored; used by core:symlink_shared \"%{deploy_to}/shared\" ssh_connect_timeout The number of seconds tomo will wait before it gives up when trying to open an SSH connection 5 ssh_executable The name (or full path) of the ssh executable \"ssh\" ssh_extra_opts An array of extra command line arguments that tomo will pass to every invocation of the ssh executable [\"-o\", \"PasswordAuthentication=no\"] ssh_forward_agent Whether to forward authentication when connecting via SSH; needed for seamless git+ssh true ssh_reuse_connections Whether to use ControlMaster to keep connections open across multiple invocations of ssh; setting this to false will slow down tomo significantly true ssh_strict_host_key_checking Use \"accept-new\" for a good compromise of security and convenience, true for most security, false for most convenience; note that older versions of ssh do not understand the \"accept-new\" option \"accept-new\" tmp_path Directory where the setup command stages temporary files \"/tmp/tomo-#{SecureRandom.alphanumeric(8)}\" tomo_config_file_path A special read-only setting containing the path to the config.rb file that was used to configure tomo \"/path/to/.tomo/config.rb\"","title":"Settings"},{"location":"plugins/core/#tasks","text":"","title":"Tasks"},{"location":"plugins/core/#coresetup_directories","text":"Creates the :deploy_to , :shared_path , and :releases_path directories so that other tasks that rely on these directories can work. This is one of the first tasks that should be run as part of setup .","title":"core:setup_directories"},{"location":"plugins/core/#coresymlink_shared","text":"Creates a symlink for each directory listed in the :linked_dirs setting and each file in :linked_files . The symlink will point to the directory or file of the same name inside the shared directory. This allows these directories and files to be shared across all releases. Note that if a directory or file already exists in the release, that directory or file will be deleted or overwritten prior to creating the link. For example, given this configuration: set linked_dirs: [\"public/assets\"] set linked_files: [\"config/database.yml\"] Calling this task will run: mkdir -p /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public cd /var/www/my-app/releases/20190604204415 && rm -rf public/assets ln -sf /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public/assets ln -sfn /var/www/my-app/shared/config/database.yml \\ /var/www/my-app/releases/20190604204415/config/database.yml core:symlink_shared is intended for use as a deploy task. If :linked_dirs and :linked_files are both empty, running this task has no effect.","title":"core:symlink_shared"},{"location":"plugins/core/#coresymlink_current","text":"Promotes the release that is currently being deployed to become the \u201ccurrent\u201d release by updating the current symlink. core:symlink_shared is intended for use as a deploy task. It is typically run after all build steps have completed ( bundler:install , rails:db_migrate , rails:assets_precompile , etc.).","title":"core:symlink_current"},{"location":"plugins/core/#coreclean_releases","text":"Deletes old releases while maintaining the most recent releases and keeping the current release. The total number of releases kept will be based on the :keep_releases setting. If this setting is absent or zero, running this task has no effect. If you are continuously deploying your application in an automated fashion, the releases can quickly fill up disk space if they are not pruned; hence the need for this task. core:clean_releases is intended for use as a deploy task. It is typically run at the end of a deployment once everything else has succeeded.","title":"core:clean_releases"},{"location":"plugins/core/#corewrite_release_json","text":"Writes a JSON file to the location specified by the :release_json_path setting. This file will contain a JSON object with properties that describe the release. Here is an example: { \"ref\": \"main\", \"author\": \"matt@example.com\", \"revision\": \"0d1cb3212e2f9c43aa49fb172d8d9c726163cecf\", \"revision_date\": \"2019-06-01 17:23:48 -0700\", \"deploy_date\": \"2019-06-05 19:00:26 -0700\", \"deploy_user\": \"mbrictson\" }","title":"core:write_release_json"},{"location":"plugins/core/#corelog_revision","text":"Appends a message to a log file specified by the :revision_log_path setting. The message contains information about the release. Here is an example entry: 2019-06-05 19:00:26 -0700 - 0d1cb3212e2f9c43aa49fb172d8d9c726163cecf (main) deployed by mbrictson","title":"core:log_revision"},{"location":"plugins/core/#helpers","text":"All of these methods are available on instances of Remote and accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/core/#remotecapturecommand-options-string","text":"Run the given command, returning the stdout of that command. If the command did not write to stdout, then return an empty String. Note that stderr is ignored, and an exception will be thrown if the command fails. remote.capture(\"echo\", \"hello\") # => \"hello\\n\"","title":"remote.capture(*command, **options) \u2192 String"},{"location":"plugins/core/#remoteruncommand-options-true-or-false","text":"Run the given command, returning true if the command succeeded (exit status of 0) or false otherwise. # If java is not installed in the $PATH remote.run?(\"which\", \"java\") # => false","title":"remote.run?(*command, **options) \u2192 true or false"},{"location":"plugins/core/#remotewritetexttemplate-to-append-false-options-tomoresult","text":"Write the given text (a String) or the text resulting from merging the given template (a local path to an ERB template file) to the remote path specified by to: . Refer to the merge_template documentation for details on tomo\u2019s ERB templating behavior. If append is false (the default), the remote file will completely replaced; if true , the file will be appended to. This is designed for small amounts of text (e.g. configuration files), not large or binary data. remote.write text: \"hello world!\\n\", to: paths.shared.join(\"greetings.txt\"), append: true remote.write template: File.expand_path(\"unicorn.service.erb\", __dir__), to: \".config/systemd/user/unicorn.service\"","title":"remote.write(text:/template:, to:, append: false, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remoteln_sftarget-link-options-tomoresult","text":"Create a symlink on the remote host at the path specified at link that points to target . Deletes any existing file that already exists at the link path prior to creating the symlink. remote.ln_sf(paths.shared.join(\".env\"), paths.release.join(\".env\")) # $ ln -sf /var/www/my-app/shared/.env /var/www/my-app/releases/20190604204415/.env","title":"remote.ln_sf(target, link, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remoteln_sfntarget-link-options-tomoresult","text":"Like ln_sf but also passes the -n flag, which allows an existing link to be deleted even if it is a symlink to a directory.","title":"remote.ln_sfn(target, link, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remotemkdir_pdirectories-options-tomoresult","text":"Creates one or more directories on the remote host. remote.mkdir_p(paths.current.dirname, paths.shared) # $ mkdir -p /var/www/my-app /var/www/my-app/shared","title":"remote.mkdir_p(*directories, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remoterm_rfpaths-options-tomoresult","text":"Deletes one or more files or directories on the remote host. remote.rm_rf(paths.tmp) # $ rm -rf /tmp/tomo-a4DBHX0P","title":"remote.rm_rf(*paths, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remotelist_filesdirectorynil-options-string","text":"Lists non-hidden files in the specified directory. If directory is omitted, the default SSH login directory is used (typically the deploy user\u2019s home directory). The result will be an array of the directory contents. remote.list_files(\"/var/www/my-app\") # => [\"current\", \"releases\", \"revision.log\", shared\"]","title":"remote.list_files(directory=nil, **options) \u2192 [String]"},{"location":"plugins/core/#remotecommand_availablecommand_name-options-true-or-false","text":"Runs which on the remote host to determine whether the given command_name is an available executable. Returns true if an executable exists, false otherwise. remote.command_available?(\"java\") # => false","title":"remote.command_available?(command_name, **options) \u2192 true or false"},{"location":"plugins/core/#remotefilefile-options-true-or-false","text":"Uses the shell expression [ -f ] to test whether the given file exists on the remote host. Returns true if the path exists and is a normal file, false otherwise. remote.file?(\".bashrc\") # => true","title":"remote.file?(file, **options) \u2192 true or false"},{"location":"plugins/core/#remoteexecutablefile-options-true-or-false","text":"Uses the shell expression [ -x ] to test whether the given file exists on the remote host. Returns true if the path exists and is executable, false otherwise. remote.executable?(\"/usr/bin/git\") # => true","title":"remote.executable?(file, **options) \u2192 true or false"},{"location":"plugins/core/#remotedirectorydirectory-options-true-or-false","text":"Uses the shell expression [ -d ] to test whether the given directory exists on the remote host. Returns true if the path exists and is a directory, false otherwise. remote.directory?(\"/opt\") # => true","title":"remote.directory?(directory, **options) \u2192 true or false"},{"location":"plugins/env/","text":"env The env plugin manages environment variables on the remote host. It does this by creating an envrc file on the remote host and modifying the .bashrc of the deploy user so that the envrc is always loaded (for both interactive and non-interactive SSH sessions). There are two ways to specify the environment variables that are stored in the envrc file: Use env:set via the command line like tomo run env:set NAME[=VALUE] ... to explicitly set or modify environment variables Specify the :env_vars setting in the tomo configuration and then run the env:update task Note that in order for these tasks to work, you must first run env:setup to ensure the deploy user\u2019s .bashrc is properly configured to read from the envrc file that is managed by this plugin. Settings Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" env_path Location of the envrc file on the remote host \"%{deploy_to}/envrc\" env_vars A hash of environment variable names and values that will configured on the remote host; see env:update for details {} Tasks env:setup Performs an env:update and then modifies the deploy user\u2019s bashrc so that the envrc is automatically loaded for all future SSH sessions. Specifically, this is what is added to the top of the .bashrc file: if [ -f /var/www/my-app/envrc ]; then . /var/www/my-app/envrc fi env:setup is intended for use as a setup task. It must be run before other env tasks. env:update Ensures that all environment variables that are specified in the :env_vars setting are present in the envrc file on the remote host, modifying the envrc file if necessary. For example, given this config: set env_vars: { RAILS_ENV: \"production\", PUMA_THREADS: 20 } This task will ensure that the envrc file is updated to include: export RAILS_ENV=production export PUMA_THREADS=20 :prompt For environment variables that are used for API keys or other sensitive data, you can specify :prompt instead of the actual value. In this case tomo will prompt interactively for the value the first time it is needed. For example: set env_vars: { DATABASE_URL: :prompt } The first time env:update is run, tomo will prompt for the value: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to user@app.example.com \u2022 env:update DATABASE_URL? Once the environment variable exists in the envrc file, tomo will no longer prompt for it. :generate_secret Similarly, for environment variables that requires a randomly generated secret value, like SECRET_KEY_BASE , you can specify :generate_secret . In this case, tomo will generate a value using SecureRandom.hex(64) the first time it is needed. set env_vars: { SECRET_KEY_BASE: :generate_secret } env:update is intended for use as a deploy task. It should be run at the beginning of a deploy to ensure that the environment has all the latest values before other tasks are run. env:set Set one or more environment variables in the remote envrc file. This task is intended for use with run and takes command-line arguments. There are two forms: # Set the remote envrc var named KEY to have VALUE $ tomo run env:set KEY=VALUE # Prompt interactively for the value of KEY and then set it in the remote envrc $ tomo run env:set KEY KEY? env:unset Remove one or more environment variables from the remote envrc file. This task is intended for use with run and takes command-line arguments. # Remove the remote envrc var named KEY $ tomo run env:unset KEY env:show Display the contents of the remote envrc file. This task is intended for use with run . $ tomo run env:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 env:show RAILS_ENV=production SECRET_KEY_BASE=02d587d76e80b2266289adef13fc045dd8387ede92935bcc1d49aa89932e5f74c35ed25bbdc41d3cf6cfc7f5f7f1736199997be459251aec52e42797c5140743 \u2714 Ran env:show on deployer@app.example.com","title":"env"},{"location":"plugins/env/#env","text":"The env plugin manages environment variables on the remote host. It does this by creating an envrc file on the remote host and modifying the .bashrc of the deploy user so that the envrc is always loaded (for both interactive and non-interactive SSH sessions). There are two ways to specify the environment variables that are stored in the envrc file: Use env:set via the command line like tomo run env:set NAME[=VALUE] ... to explicitly set or modify environment variables Specify the :env_vars setting in the tomo configuration and then run the env:update task Note that in order for these tasks to work, you must first run env:setup to ensure the deploy user\u2019s .bashrc is properly configured to read from the envrc file that is managed by this plugin.","title":"env"},{"location":"plugins/env/#settings","text":"Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" env_path Location of the envrc file on the remote host \"%{deploy_to}/envrc\" env_vars A hash of environment variable names and values that will configured on the remote host; see env:update for details {}","title":"Settings"},{"location":"plugins/env/#tasks","text":"","title":"Tasks"},{"location":"plugins/env/#envsetup","text":"Performs an env:update and then modifies the deploy user\u2019s bashrc so that the envrc is automatically loaded for all future SSH sessions. Specifically, this is what is added to the top of the .bashrc file: if [ -f /var/www/my-app/envrc ]; then . /var/www/my-app/envrc fi env:setup is intended for use as a setup task. It must be run before other env tasks.","title":"env:setup"},{"location":"plugins/env/#envupdate","text":"Ensures that all environment variables that are specified in the :env_vars setting are present in the envrc file on the remote host, modifying the envrc file if necessary. For example, given this config: set env_vars: { RAILS_ENV: \"production\", PUMA_THREADS: 20 } This task will ensure that the envrc file is updated to include: export RAILS_ENV=production export PUMA_THREADS=20","title":"env:update"},{"location":"plugins/env/#prompt","text":"For environment variables that are used for API keys or other sensitive data, you can specify :prompt instead of the actual value. In this case tomo will prompt interactively for the value the first time it is needed. For example: set env_vars: { DATABASE_URL: :prompt } The first time env:update is run, tomo will prompt for the value: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to user@app.example.com \u2022 env:update DATABASE_URL? Once the environment variable exists in the envrc file, tomo will no longer prompt for it.","title":":prompt"},{"location":"plugins/env/#generate_secret","text":"Similarly, for environment variables that requires a randomly generated secret value, like SECRET_KEY_BASE , you can specify :generate_secret . In this case, tomo will generate a value using SecureRandom.hex(64) the first time it is needed. set env_vars: { SECRET_KEY_BASE: :generate_secret } env:update is intended for use as a deploy task. It should be run at the beginning of a deploy to ensure that the environment has all the latest values before other tasks are run.","title":":generate_secret"},{"location":"plugins/env/#envset","text":"Set one or more environment variables in the remote envrc file. This task is intended for use with run and takes command-line arguments. There are two forms: # Set the remote envrc var named KEY to have VALUE $ tomo run env:set KEY=VALUE # Prompt interactively for the value of KEY and then set it in the remote envrc $ tomo run env:set KEY KEY?","title":"env:set"},{"location":"plugins/env/#envunset","text":"Remove one or more environment variables from the remote envrc file. This task is intended for use with run and takes command-line arguments. # Remove the remote envrc var named KEY $ tomo run env:unset KEY","title":"env:unset"},{"location":"plugins/env/#envshow","text":"Display the contents of the remote envrc file. This task is intended for use with run . $ tomo run env:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 env:show RAILS_ENV=production SECRET_KEY_BASE=02d587d76e80b2266289adef13fc045dd8387ede92935bcc1d49aa89932e5f74c35ed25bbdc41d3cf6cfc7f5f7f1736199997be459251aec52e42797c5140743 \u2714 Ran env:show on deployer@app.example.com","title":"env:show"},{"location":"plugins/git/","text":"git The git plugin uses git running on the remote host to fetch the code of the app being deployed. This \u201cremote pull\u201d technique is currently the only deployment method officially supported by tomo. For this to work, the SSH key you use to connect to the remote host via tomo must match the key expected by the git host (e.g. by GitHub). Settings Name Purpose Default git_branch The branch of the repository to deploy nil git_repo_path Directory on the remote host where a cache of the repository will be stored \"%{deploy_to}/git_repo\" git_exclusions An array of paths (similar to gitignore syntax) that will be excluded when the repository is copied into a release; it is recommend you exclude .tomo/ and other directories not needed in production, like spec/ [] git_env Environment variables that will be set when issuing git commands (hash) { GIT_SSH_COMMAND: \"ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no\" } git_ref The commit SHA or tag to deploy (overrides :git_branch ) nil git_url URL of the git repository; always use the SSH form like git@github.com:username/repo.git (not HTTPS) nil git_user_name The value to use for git\u2019s user.name config nil git_user_email The value to use for git\u2019s user.email config nil Tasks git:config Globally (for the deployer user) configures git with values for user.name and user.email so that certain git commands are able to function. By default, tomo will use the deployer username and append @example.com to generate these values, like this: git config --global user.name deployer git config --global user.email deployer@example.com If you wish to customize the name and email values, use the git_user_name and git_user_email settings. git:config is intended for use as a setup task. git:clone Performs the initial clone of the git repository. This is necessary before a deploy can be performed. The clone of the repository will be stored in the git_repo_path . The git_url setting must be specified for this task to work. git:clone is intended for use as a setup task. git:create_release Fetches the latest commits from git_branch (or git_ref ) and creates a release by copying the contents of that branch of repository into a new release inside the releases_path . Releases are numbered based on the timestamp of when the deploy takes place. git:create_release is intended for use as a deploy task. Helpers These helper methods become available on instances of Remote when the git plugin is loaded. They accept the same options as Remote#run . remote.git(*args, **options) \u2192 Tomo::Result Runs git with the environment variables specified by the git_env setting. remote.git(\"fetch\") # $ export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication=no\\ -o\\ StrictHostKeyChecking=no && git fetch","title":"git"},{"location":"plugins/git/#git","text":"The git plugin uses git running on the remote host to fetch the code of the app being deployed. This \u201cremote pull\u201d technique is currently the only deployment method officially supported by tomo. For this to work, the SSH key you use to connect to the remote host via tomo must match the key expected by the git host (e.g. by GitHub).","title":"git"},{"location":"plugins/git/#settings","text":"Name Purpose Default git_branch The branch of the repository to deploy nil git_repo_path Directory on the remote host where a cache of the repository will be stored \"%{deploy_to}/git_repo\" git_exclusions An array of paths (similar to gitignore syntax) that will be excluded when the repository is copied into a release; it is recommend you exclude .tomo/ and other directories not needed in production, like spec/ [] git_env Environment variables that will be set when issuing git commands (hash) { GIT_SSH_COMMAND: \"ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no\" } git_ref The commit SHA or tag to deploy (overrides :git_branch ) nil git_url URL of the git repository; always use the SSH form like git@github.com:username/repo.git (not HTTPS) nil git_user_name The value to use for git\u2019s user.name config nil git_user_email The value to use for git\u2019s user.email config nil","title":"Settings"},{"location":"plugins/git/#tasks","text":"","title":"Tasks"},{"location":"plugins/git/#gitconfig","text":"Globally (for the deployer user) configures git with values for user.name and user.email so that certain git commands are able to function. By default, tomo will use the deployer username and append @example.com to generate these values, like this: git config --global user.name deployer git config --global user.email deployer@example.com If you wish to customize the name and email values, use the git_user_name and git_user_email settings. git:config is intended for use as a setup task.","title":"git:config"},{"location":"plugins/git/#gitclone","text":"Performs the initial clone of the git repository. This is necessary before a deploy can be performed. The clone of the repository will be stored in the git_repo_path . The git_url setting must be specified for this task to work. git:clone is intended for use as a setup task.","title":"git:clone"},{"location":"plugins/git/#gitcreate_release","text":"Fetches the latest commits from git_branch (or git_ref ) and creates a release by copying the contents of that branch of repository into a new release inside the releases_path . Releases are numbered based on the timestamp of when the deploy takes place. git:create_release is intended for use as a deploy task.","title":"git:create_release"},{"location":"plugins/git/#helpers","text":"These helper methods become available on instances of Remote when the git plugin is loaded. They accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/git/#remotegitargs-options-tomoresult","text":"Runs git with the environment variables specified by the git_env setting. remote.git(\"fetch\") # $ export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication=no\\ -o\\ StrictHostKeyChecking=no && git fetch","title":"remote.git(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/nodenv/","text":"nodenv The nodenv plugin installs node and yarn. This allows you to deploy an app with confidence that yarn and a particular version of node will be available on the host. This plugin is strongly recommended for Rails apps, which by default use webpacker and thus require node and yarn. Settings Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" nodenv_install_yarn Whether to install yarn globally via npm i -g yarn true nodenv_node_version Version of node to install; if nil (the default), determine the version based on .node-version nil nodenv_yarn_version A value of nil (the default) means install the latest; specify this only if you need a specific 1.y.z global version of yarn nil Tasks nodenv:install Installs nodenv, uses nodenv to install node, and makes the desired version of node the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that nodenv is automatically loaded for interactive and non-interactive shells. You must supply a value for the nodenv_node_version setting or have a .node-version file in your project for this task to work. By default, yarn is also installed globally via npm. This can be disabled by setting nodenv_install_yarn to false . nodenv:install is intended for use as a setup task.","title":"nodenv"},{"location":"plugins/nodenv/#nodenv","text":"The nodenv plugin installs node and yarn. This allows you to deploy an app with confidence that yarn and a particular version of node will be available on the host. This plugin is strongly recommended for Rails apps, which by default use webpacker and thus require node and yarn.","title":"nodenv"},{"location":"plugins/nodenv/#settings","text":"Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" nodenv_install_yarn Whether to install yarn globally via npm i -g yarn true nodenv_node_version Version of node to install; if nil (the default), determine the version based on .node-version nil nodenv_yarn_version A value of nil (the default) means install the latest; specify this only if you need a specific 1.y.z global version of yarn nil","title":"Settings"},{"location":"plugins/nodenv/#tasks","text":"","title":"Tasks"},{"location":"plugins/nodenv/#nodenvinstall","text":"Installs nodenv, uses nodenv to install node, and makes the desired version of node the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that nodenv is automatically loaded for interactive and non-interactive shells. You must supply a value for the nodenv_node_version setting or have a .node-version file in your project for this task to work. By default, yarn is also installed globally via npm. This can be disabled by setting nodenv_install_yarn to false . nodenv:install is intended for use as a setup task.","title":"nodenv:install"},{"location":"plugins/puma/","text":"puma The puma plugin provides a systemd -based solution for starting, stopping, and restarting puma using socket activation for zero-downtime restarts. It is based on the best practices in the official puma documentation . Tomo\u2019s implementation installs puma as a user-level service using systemctl --user . This allows puma to be installed, started, stopped, and restarted without a root user or sudo . However, when provisioning the host you must make sure to run the following command as root to allow the puma process to continue running even after the tomo deploy user disconnects: # run as root $ loginctl enable-linger Stdout and stderr of the puma process will be routed to syslog, as is the convention for systemd services. For Rails, it is recommended that you set RAILS_LOG_TO_STDOUT=1 so that all Rails logs are handled this way ( tomo init configures this by default). The tomo puma plugin assumes that your puma server will listen on a single TCP port for HTTP (not HTTPS) traffic. In other words, HTTPS termination will be handled by e.g. Nginx or a separate load balancer. Settings Name Purpose Default puma_check_timeout The number of seconds that the puma:check_active task will wait for puma to respond before timing out. 15 puma_host Hostname / IP address that puma should listen on 0.0.0.0 (set to 127.0.0.1 to accept only internal connections) puma_port TCP port that puma should listen on 3000 puma_systemd_service Name of the systemd service that manages the puma server \"puma_%{application}.service\" puma_systemd_socket Name of the systemd socket that is used for socket activation of the puma service \"puma_%{application}.socket\" puma_systemd_service_path Path on the remote host where the systemd puma service configuration file will be created \".config/systemd/user/%{puma_systemd_service}\" puma_systemd_service_type If set to \"notify\" , Puma will automatically be restarted if it locks up; change to \"simple\" if using JRuby or Puma < 5.1 \"notify\" puma_systemd_socket_path Path on the remote host where the systemd puma socket configuration file will be created \".config/systemd/user/%{puma_systemd_socket}\" puma_systemd_service_template_path Local path of the ERB template to use to create the the systemd puma service configuration file service.erb puma_systemd_socket_template_path Local path of the ERB template to use to create the the systemd puma socket configuration file socket.erb Tasks puma:setup_systemd Configures systemd to manage puma. This means that puma will automatically be restarted if it crashes, or if the host is rebooted. This task essentially does three things: Installs a puma.socket systemd unit Installs a puma.service systemd unit that depends on the socket Enables these units using systemctl --user enable Note that these units will be installed and run for the deploy user. You can use :puma_systemd_socket_template_path and :puma_systemd_service_template_path to provide your own templates and customize how puma and systemd are configured. puma:setup_systemd is intended for use as a setup task. It must be run before puma can be started during a deploy. puma:restart Restarts the puma service via systemd. This starts puma if it isn\u2019t running already. The systemd socket remains running while puma itself is restarted. In other words, incoming requests will continue to connect and queue while puma restarts. This is a \u201czero-downtime restart\u201d. Puma will be configured to listen on :puma_port , with the config/puma.rb file within the Rails app providing the remainder of the configuration. The default port is 3000. Puma is started using this command: bundle exec --keep-file-descriptors puma -C config/puma.rb -b tcp://0.0.0.0:3000 puma:restart is intended for use in a deploy , immediately following core:symlink_current to ensure that the new version of the Rails app is activated. puma:check_active This task queries systemd and executes a curl test to verify that puma is active and listening on :puma_port . Because puma is run in the background, it is not immediately obvious after starting or restarting puma via systemd as to whether it booted successfully, or if it crashed. This is where the puma:check_active task can help. If puma is not working it will fail and show puma\u2019s log output for easier troubleshooting. puma:check_active is intended for use as a deploy task, immediately following puma:restart to verify that puma restarted successfully. puma:start Starts the puma socket and service via systemd, if they aren\u2019t running already. Equivalent to: systemctl --user start puma.socket puma.service puma:stop Stops the puma socket and service via systemd. Equivalent to: systemctl --user stop puma.socket puma.service puma:status Reports the status of the puma socket and service via systemd. Equivalent to: systemctl --user status puma.socket puma.service Sample output: $ tomo run puma:status tomo run v0.10.0 \u2192 Connecting to deployer@app.example.com \u2022 puma:status systemctl --user status puma_example.socket puma_example.service \u25cf puma_example.socket - Puma HTTP Server Accept Sockets for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.socket; enabled; vendor preset: enabled) Active: active (running) since Thu 2019-10-24 09:41:53 UTC; 1 weeks 2 days ago Listen: 0.0.0.0:3000 (Stream) \u25cf puma_example.service - Puma HTTP Server for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2019-11-01 15:46:10 UTC; 1 day 10h ago Main PID: 14513 (bundle) CGroup: /user.slice/user-1000.slice/user@1000.service/puma_example.service \u2514\u250014513 puma 4.2.1 (tcp://0.0.0.0:3000) [20191101154450] puma:log Uses journalctl (part of systemd) to view the log output of the puma service. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the journalctl command. For example: $ tomo run -- puma:log -f Will run this remote script: journalctl -q --user-unit=puma.service -f puma:tail_log A convenience method for tailing the puma logs. Equivalent to tomo run -- puma:log -f","title":"puma"},{"location":"plugins/puma/#puma","text":"The puma plugin provides a systemd -based solution for starting, stopping, and restarting puma using socket activation for zero-downtime restarts. It is based on the best practices in the official puma documentation . Tomo\u2019s implementation installs puma as a user-level service using systemctl --user . This allows puma to be installed, started, stopped, and restarted without a root user or sudo . However, when provisioning the host you must make sure to run the following command as root to allow the puma process to continue running even after the tomo deploy user disconnects: # run as root $ loginctl enable-linger Stdout and stderr of the puma process will be routed to syslog, as is the convention for systemd services. For Rails, it is recommended that you set RAILS_LOG_TO_STDOUT=1 so that all Rails logs are handled this way ( tomo init configures this by default). The tomo puma plugin assumes that your puma server will listen on a single TCP port for HTTP (not HTTPS) traffic. In other words, HTTPS termination will be handled by e.g. Nginx or a separate load balancer.","title":"puma"},{"location":"plugins/puma/#settings","text":"Name Purpose Default puma_check_timeout The number of seconds that the puma:check_active task will wait for puma to respond before timing out. 15 puma_host Hostname / IP address that puma should listen on 0.0.0.0 (set to 127.0.0.1 to accept only internal connections) puma_port TCP port that puma should listen on 3000 puma_systemd_service Name of the systemd service that manages the puma server \"puma_%{application}.service\" puma_systemd_socket Name of the systemd socket that is used for socket activation of the puma service \"puma_%{application}.socket\" puma_systemd_service_path Path on the remote host where the systemd puma service configuration file will be created \".config/systemd/user/%{puma_systemd_service}\" puma_systemd_service_type If set to \"notify\" , Puma will automatically be restarted if it locks up; change to \"simple\" if using JRuby or Puma < 5.1 \"notify\" puma_systemd_socket_path Path on the remote host where the systemd puma socket configuration file will be created \".config/systemd/user/%{puma_systemd_socket}\" puma_systemd_service_template_path Local path of the ERB template to use to create the the systemd puma service configuration file service.erb puma_systemd_socket_template_path Local path of the ERB template to use to create the the systemd puma socket configuration file socket.erb","title":"Settings"},{"location":"plugins/puma/#tasks","text":"","title":"Tasks"},{"location":"plugins/puma/#pumasetup_systemd","text":"Configures systemd to manage puma. This means that puma will automatically be restarted if it crashes, or if the host is rebooted. This task essentially does three things: Installs a puma.socket systemd unit Installs a puma.service systemd unit that depends on the socket Enables these units using systemctl --user enable Note that these units will be installed and run for the deploy user. You can use :puma_systemd_socket_template_path and :puma_systemd_service_template_path to provide your own templates and customize how puma and systemd are configured. puma:setup_systemd is intended for use as a setup task. It must be run before puma can be started during a deploy.","title":"puma:setup_systemd"},{"location":"plugins/puma/#pumarestart","text":"Restarts the puma service via systemd. This starts puma if it isn\u2019t running already. The systemd socket remains running while puma itself is restarted. In other words, incoming requests will continue to connect and queue while puma restarts. This is a \u201czero-downtime restart\u201d. Puma will be configured to listen on :puma_port , with the config/puma.rb file within the Rails app providing the remainder of the configuration. The default port is 3000. Puma is started using this command: bundle exec --keep-file-descriptors puma -C config/puma.rb -b tcp://0.0.0.0:3000 puma:restart is intended for use in a deploy , immediately following core:symlink_current to ensure that the new version of the Rails app is activated.","title":"puma:restart"},{"location":"plugins/puma/#pumacheck_active","text":"This task queries systemd and executes a curl test to verify that puma is active and listening on :puma_port . Because puma is run in the background, it is not immediately obvious after starting or restarting puma via systemd as to whether it booted successfully, or if it crashed. This is where the puma:check_active task can help. If puma is not working it will fail and show puma\u2019s log output for easier troubleshooting. puma:check_active is intended for use as a deploy task, immediately following puma:restart to verify that puma restarted successfully.","title":"puma:check_active"},{"location":"plugins/puma/#pumastart","text":"Starts the puma socket and service via systemd, if they aren\u2019t running already. Equivalent to: systemctl --user start puma.socket puma.service","title":"puma:start"},{"location":"plugins/puma/#pumastop","text":"Stops the puma socket and service via systemd. Equivalent to: systemctl --user stop puma.socket puma.service","title":"puma:stop"},{"location":"plugins/puma/#pumastatus","text":"Reports the status of the puma socket and service via systemd. Equivalent to: systemctl --user status puma.socket puma.service Sample output: $ tomo run puma:status tomo run v0.10.0 \u2192 Connecting to deployer@app.example.com \u2022 puma:status systemctl --user status puma_example.socket puma_example.service \u25cf puma_example.socket - Puma HTTP Server Accept Sockets for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.socket; enabled; vendor preset: enabled) Active: active (running) since Thu 2019-10-24 09:41:53 UTC; 1 weeks 2 days ago Listen: 0.0.0.0:3000 (Stream) \u25cf puma_example.service - Puma HTTP Server for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2019-11-01 15:46:10 UTC; 1 day 10h ago Main PID: 14513 (bundle) CGroup: /user.slice/user-1000.slice/user@1000.service/puma_example.service \u2514\u250014513 puma 4.2.1 (tcp://0.0.0.0:3000) [20191101154450]","title":"puma:status"},{"location":"plugins/puma/#pumalog","text":"Uses journalctl (part of systemd) to view the log output of the puma service. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the journalctl command. For example: $ tomo run -- puma:log -f Will run this remote script: journalctl -q --user-unit=puma.service -f","title":"puma:log"},{"location":"plugins/puma/#pumatail_log","text":"A convenience method for tailing the puma logs. Equivalent to tomo run -- puma:log -f","title":"puma:tail_log"},{"location":"plugins/rails/","text":"rails The rails plugin provides tasks for running rails and rake commands commonly used during setup and deployment, such as for precompiling assets and migrating the database. Make sure the RAILS_ENV environment variable is set prior to running rails tasks. The env plugin is the preferred mechanism for this. Settings None. Tasks rails:assets_precompile Builds the asset pipeline in preparation for deployment. This is necessary for Rails apps that use the asset pipeline, which is all new Rails apps by default. Running this task will execute this script: cd /var/www/my-app/releases/ && bundle exec rake assets:precompile rails:assets_precompile is intended for use as a deploy task. It is typically run just prior to core:symlink_current to activate a new release. rails:console Starts an interactive Rails console via SSH to the remote host. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the console. For example: $ tomo run -- rails:console --sandbox Will run this remote script: cd /var/www/my-app/current && bundle exec rails console --sandbox rails:db_console Starts an interactive database console (e.g. psql) for the primary Rails database via SSH to the remote host. This task is intended for use as a run task. The include-password option is passed automatically. $ tomo run rails:db_console Will run this remote script: cd /var/www/my-app/current && bundle exec rails dbconsole --include-password rails:db_migrate Migrates the database by running: cd /var/www/my-app/releases/ && bundle exec rake db:migrate rails:db_migrate is intended for use as a deploy task. It is typically run just after bundler:install prior to activating a new release. rails:db_seed Loads seed data into the database. Seeds should be written to be idempotent, such that it is safe to seed the database on each deploy. Typically seeds are used to load reference data need for the app to function, or for example to create an initial admin user. This task runs the following script: cd /var/www/my-app/releases/ && bundle exec rake db:seed rails:db_seed is intended for use as a deploy task. Since seeds rely on the structure of the database, it is typically run just after rails:db_migrate . rails:db_create Runs bundle exec rake db:create to create the database. This task is intended for use as a setup task. It will be automatically skipped if the database already exists, so it is safe to re-run. rails:db_schema_load Runs bundle exec rake db:schema:load to load the schema from db/schema.rb into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run. rails:db_structure_load Runs bundle exec rake db:structure:load to load the schema from db/structure.sql into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run. Helpers These helper methods become available on instances of Remote when the rails plugin is loaded. They accept the same options as Remote#run . remote.rails(*args, **options) \u2192 Tomo::Result Runs bundle exec rails in within paths.release by default. remote.rails(\"routes\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rails routes remote.rake(*args, **options) \u2192 Tomo::Result Runs bundle exec rake in within paths.release by default. remote.rake(\"db:migrate\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rake db:migrate remote.rake?(*args, **options) \u2192 true or false Like rake but returns true if the command succeeds (exit status 0), otherwise false . remote.rake?(\"db:migrate\") # => true remote.thor(*args, **options) \u2192 Tomo::Result Runs bundle exec thor in within paths.release by default. remote.thor(\"user:create\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec thor user:create","title":"rails"},{"location":"plugins/rails/#rails","text":"The rails plugin provides tasks for running rails and rake commands commonly used during setup and deployment, such as for precompiling assets and migrating the database. Make sure the RAILS_ENV environment variable is set prior to running rails tasks. The env plugin is the preferred mechanism for this.","title":"rails"},{"location":"plugins/rails/#settings","text":"None.","title":"Settings"},{"location":"plugins/rails/#tasks","text":"","title":"Tasks"},{"location":"plugins/rails/#railsassets_precompile","text":"Builds the asset pipeline in preparation for deployment. This is necessary for Rails apps that use the asset pipeline, which is all new Rails apps by default. Running this task will execute this script: cd /var/www/my-app/releases/ && bundle exec rake assets:precompile rails:assets_precompile is intended for use as a deploy task. It is typically run just prior to core:symlink_current to activate a new release.","title":"rails:assets_precompile"},{"location":"plugins/rails/#railsconsole","text":"Starts an interactive Rails console via SSH to the remote host. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the console. For example: $ tomo run -- rails:console --sandbox Will run this remote script: cd /var/www/my-app/current && bundle exec rails console --sandbox","title":"rails:console"},{"location":"plugins/rails/#railsdb_console","text":"Starts an interactive database console (e.g. psql) for the primary Rails database via SSH to the remote host. This task is intended for use as a run task. The include-password option is passed automatically. $ tomo run rails:db_console Will run this remote script: cd /var/www/my-app/current && bundle exec rails dbconsole --include-password","title":"rails:db_console"},{"location":"plugins/rails/#railsdb_migrate","text":"Migrates the database by running: cd /var/www/my-app/releases/ && bundle exec rake db:migrate rails:db_migrate is intended for use as a deploy task. It is typically run just after bundler:install prior to activating a new release.","title":"rails:db_migrate"},{"location":"plugins/rails/#railsdb_seed","text":"Loads seed data into the database. Seeds should be written to be idempotent, such that it is safe to seed the database on each deploy. Typically seeds are used to load reference data need for the app to function, or for example to create an initial admin user. This task runs the following script: cd /var/www/my-app/releases/ && bundle exec rake db:seed rails:db_seed is intended for use as a deploy task. Since seeds rely on the structure of the database, it is typically run just after rails:db_migrate .","title":"rails:db_seed"},{"location":"plugins/rails/#railsdb_create","text":"Runs bundle exec rake db:create to create the database. This task is intended for use as a setup task. It will be automatically skipped if the database already exists, so it is safe to re-run.","title":"rails:db_create"},{"location":"plugins/rails/#railsdb_schema_load","text":"Runs bundle exec rake db:schema:load to load the schema from db/schema.rb into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run.","title":"rails:db_schema_load"},{"location":"plugins/rails/#railsdb_structure_load","text":"Runs bundle exec rake db:structure:load to load the schema from db/structure.sql into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run.","title":"rails:db_structure_load"},{"location":"plugins/rails/#helpers","text":"These helper methods become available on instances of Remote when the rails plugin is loaded. They accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/rails/#remoterailsargs-options-tomoresult","text":"Runs bundle exec rails in within paths.release by default. remote.rails(\"routes\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rails routes","title":"remote.rails(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/rails/#remoterakeargs-options-tomoresult","text":"Runs bundle exec rake in within paths.release by default. remote.rake(\"db:migrate\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rake db:migrate","title":"remote.rake(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/rails/#remoterakeargs-options-true-or-false","text":"Like rake but returns true if the command succeeds (exit status 0), otherwise false . remote.rake?(\"db:migrate\") # => true","title":"remote.rake?(*args, **options) \u2192 true or false"},{"location":"plugins/rails/#remotethorargs-options-tomoresult","text":"Runs bundle exec thor in within paths.release by default. remote.thor(\"user:create\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec thor user:create","title":"remote.thor(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/rbenv/","text":"rbenv The rbenv plugin provides a way to install and run a desired version of ruby. This is the recommended way to manage ruby for Rails apps. Settings Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" rbenv_ruby_version Version of ruby to install; if nil (the default), determine the version based on .ruby-version nil Tasks rbenv:install Installs rbenv, uses rbenv to install ruby, and makes the desired version of ruby the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that rbenv is automatically loaded for interactive and non-interactive shells. Behind the scenes, rbenv installs ruby via ruby-build, which compiles ruby from source. This means installation can take several minutes. If the desired version of ruby is already installed, the compilation step will be skipped. You must supply a value for the rbenv_ruby_version setting or have a .ruby-version file in your project for this task to work. rbenv:install is intended for use as a setup task.","title":"rbenv"},{"location":"plugins/rbenv/#rbenv","text":"The rbenv plugin provides a way to install and run a desired version of ruby. This is the recommended way to manage ruby for Rails apps.","title":"rbenv"},{"location":"plugins/rbenv/#settings","text":"Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" rbenv_ruby_version Version of ruby to install; if nil (the default), determine the version based on .ruby-version nil","title":"Settings"},{"location":"plugins/rbenv/#tasks","text":"","title":"Tasks"},{"location":"plugins/rbenv/#rbenvinstall","text":"Installs rbenv, uses rbenv to install ruby, and makes the desired version of ruby the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that rbenv is automatically loaded for interactive and non-interactive shells. Behind the scenes, rbenv installs ruby via ruby-build, which compiles ruby from source. This means installation can take several minutes. If the desired version of ruby is already installed, the compilation step will be skipped. You must supply a value for the rbenv_ruby_version setting or have a .ruby-version file in your project for this task to work. rbenv:install is intended for use as a setup task.","title":"rbenv:install"},{"location":"readme_images/","text":"The images in this directory can be regenerated using the record_console.rb script. For example: ./record_console.rb tomo deploy --help This will translate the output of the tomo deploy --help command into HTML and launch it in a browser. From there, press the Convert to PNG button to generate an image appropriately sized for a GitHub README.","title":"Index"},{"location":"tutorials/deploying-rails-from-scratch/","text":"Deploying Rails From Scratch In this tutorial we will use tomo to deploy a sample Rails project to a virtual private server (VPS). These instructions use DigitalOcean as the hosting provider, but any provider that offers an Ubuntu 20.04 or 22.04 LTS VPS should work in a similar way. Here are the steps involved (step 1 is the only part that is DigitalOcean-specific): Create an Ubuntu VPS Install necessary apt packages Set up a deployer user Configure tomo Run tomo setup Run tomo deploy This is a basic tutorial that skips over DNS, TLS, load balancing, PostgreSQL, etc. If you have suggestions for expanding this guide, consider opening an issue or pull request on GitHub . Thanks for reading! Create an Ubuntu VPS Log into DigitalOcean and create a \u201cDroplet\u201d (aka a VPS). If this is your first time using DigitalOcean, check out their Droplet QuickStart guide for an introduction to the service. When creating the Droplet, make sure to choose Ubuntu 20.04 or 22.04 (LTS) x64 : And use SSH keys for authentication (tomo does not work with password authentication): And select a Droplet size with at least 1GB RAM , or Ruby may fail to compile: Once the Droplet is created, confirm that you are able to connect to it (substitute IPADDR with the IP address provided in the DigitalOcean control panel for the new Droplet): # Run on your local machine $ ssh -o PasswordAuthentication=no root@IPADDR echo \"authenticated as root!\" authenticated as root! You may need to type yes when prompted to trust the host fingerprint. Install necessary apt packages Rails requires certain operating system packages in order to build Ruby and install various gems that have native extensions. Connect to the VPS as root and install the following: # Run these commands as root on the VPS apt-get -y update apt-get -y install autoconf \\ bison \\ build-essential \\ curl \\ git-core \\ libdb-dev \\ libffi-dev \\ libgdbm-dev \\ libgdbm6 \\ libgmp-dev \\ libncurses5-dev \\ libreadline6-dev \\ libsqlite3-dev \\ libssl-dev \\ libyaml-dev \\ locales \\ patch \\ pkg-config \\ rustc \\ uuid-dev \\ zlib1g-dev \\ tzdata locale-gen en_US.UTF-8 It may take a minute or two for all the packages to install. Set up a deployer user Running a Rails app as root is a security risk; we need to create a non-privileged user for this purpose. Run the following script to create a deployer user that: has access to write to a /var/www directory, which is the default location where tomo will deploy our app; and can \u201clinger\u201d, i.e. run long-running processes like the puma web server # Run these commands as root on the VPS adduser --disabled-password deployer < /dev/null mkdir -p /home/deployer/.ssh cp /root/.ssh/authorized_keys /home/deployer/.ssh chown -R deployer:deployer /home/deployer/.ssh chmod 600 /home/deployer/.ssh/authorized_keys mkdir -p /var/www chown deployer:deployer /var/www loginctl enable-linger deployer For convenience, the deployer user will accept the same SSH key that you are already using to authenticate when connecting as root . Test that it works: # Run on your local machine $ ssh -o PasswordAuthentication=no deployer@IPADDR echo \"authenticated as deployer!\" authenticated as deployer! Configure tomo We will be deploying this basic Rails app . It is a standard Rails app generated by rails new , with the exception that force_ssl has been set to false for the production environment. This will allow us to deploy the app without having to set up HTTPS, which is outside the scope of this tutorial. Clone the repository to get started: # Run on your local machine $ git clone https://github.com/mattbrictson/rails-new Inside the rails-new directory, install tomo and run tomo init . This will set up a deploy configuration with a good set of defaults: # Run on your local machine $ cd rails-new $ gem install tomo Fetching tomo-1.11.0.gem Successfully installed tomo-1.11.0 1 gem installed $ tomo init \u2714 Created .tomo/config.rb The .tomo/config.rb configuration is ready right out of the box. All you will need to change is the host line, and be sure to replace IPADDR with the IP address of your VPS: # Modify the 'host' line of .tomo/config.rb host \"deployer@IPADDR\" Run tomo setup Tomo comes with a setup command that will prepare your VPS for its first deployment. This does things like install Node, Yarn, and Ruby. It also sets up some important environment variables. Just run: # Run on your local machine $ tomo setup You will be asked to specify a value for the DATABASE_URL environment variable. Tomo will store this value on the VPS so that you only have to provide it once. Provide this value when prompted: sqlite3:/var/www/rails-new/shared/production.sqlite3 . Note that tomo setup compiles Ruby from source, which will take several minutes. Run tomo deploy Once setup completes, your VPS is ready to go. Deploying is now just a matter of running tomo deploy : # Run on your local machine $ tomo deploy When the deploy completes, the Rails app will be accessible on port 3000 of your VPS: http://IPADDR:3000 . Congratulations!","title":"Deploying Rails From Scratch"},{"location":"tutorials/deploying-rails-from-scratch/#deploying-rails-from-scratch","text":"In this tutorial we will use tomo to deploy a sample Rails project to a virtual private server (VPS). These instructions use DigitalOcean as the hosting provider, but any provider that offers an Ubuntu 20.04 or 22.04 LTS VPS should work in a similar way. Here are the steps involved (step 1 is the only part that is DigitalOcean-specific): Create an Ubuntu VPS Install necessary apt packages Set up a deployer user Configure tomo Run tomo setup Run tomo deploy This is a basic tutorial that skips over DNS, TLS, load balancing, PostgreSQL, etc. If you have suggestions for expanding this guide, consider opening an issue or pull request on GitHub . Thanks for reading!","title":"Deploying Rails From Scratch"},{"location":"tutorials/deploying-rails-from-scratch/#create-an-ubuntu-vps","text":"Log into DigitalOcean and create a \u201cDroplet\u201d (aka a VPS). If this is your first time using DigitalOcean, check out their Droplet QuickStart guide for an introduction to the service. When creating the Droplet, make sure to choose Ubuntu 20.04 or 22.04 (LTS) x64 : And use SSH keys for authentication (tomo does not work with password authentication): And select a Droplet size with at least 1GB RAM , or Ruby may fail to compile: Once the Droplet is created, confirm that you are able to connect to it (substitute IPADDR with the IP address provided in the DigitalOcean control panel for the new Droplet): # Run on your local machine $ ssh -o PasswordAuthentication=no root@IPADDR echo \"authenticated as root!\" authenticated as root! You may need to type yes when prompted to trust the host fingerprint.","title":"Create an Ubuntu VPS"},{"location":"tutorials/deploying-rails-from-scratch/#install-necessary-apt-packages","text":"Rails requires certain operating system packages in order to build Ruby and install various gems that have native extensions. Connect to the VPS as root and install the following: # Run these commands as root on the VPS apt-get -y update apt-get -y install autoconf \\ bison \\ build-essential \\ curl \\ git-core \\ libdb-dev \\ libffi-dev \\ libgdbm-dev \\ libgdbm6 \\ libgmp-dev \\ libncurses5-dev \\ libreadline6-dev \\ libsqlite3-dev \\ libssl-dev \\ libyaml-dev \\ locales \\ patch \\ pkg-config \\ rustc \\ uuid-dev \\ zlib1g-dev \\ tzdata locale-gen en_US.UTF-8 It may take a minute or two for all the packages to install.","title":"Install necessary apt packages"},{"location":"tutorials/deploying-rails-from-scratch/#set-up-a-deployer-user","text":"Running a Rails app as root is a security risk; we need to create a non-privileged user for this purpose. Run the following script to create a deployer user that: has access to write to a /var/www directory, which is the default location where tomo will deploy our app; and can \u201clinger\u201d, i.e. run long-running processes like the puma web server # Run these commands as root on the VPS adduser --disabled-password deployer < /dev/null mkdir -p /home/deployer/.ssh cp /root/.ssh/authorized_keys /home/deployer/.ssh chown -R deployer:deployer /home/deployer/.ssh chmod 600 /home/deployer/.ssh/authorized_keys mkdir -p /var/www chown deployer:deployer /var/www loginctl enable-linger deployer For convenience, the deployer user will accept the same SSH key that you are already using to authenticate when connecting as root . Test that it works: # Run on your local machine $ ssh -o PasswordAuthentication=no deployer@IPADDR echo \"authenticated as deployer!\" authenticated as deployer!","title":"Set up a deployer user"},{"location":"tutorials/deploying-rails-from-scratch/#configure-tomo","text":"We will be deploying this basic Rails app . It is a standard Rails app generated by rails new , with the exception that force_ssl has been set to false for the production environment. This will allow us to deploy the app without having to set up HTTPS, which is outside the scope of this tutorial. Clone the repository to get started: # Run on your local machine $ git clone https://github.com/mattbrictson/rails-new Inside the rails-new directory, install tomo and run tomo init . This will set up a deploy configuration with a good set of defaults: # Run on your local machine $ cd rails-new $ gem install tomo Fetching tomo-1.11.0.gem Successfully installed tomo-1.11.0 1 gem installed $ tomo init \u2714 Created .tomo/config.rb The .tomo/config.rb configuration is ready right out of the box. All you will need to change is the host line, and be sure to replace IPADDR with the IP address of your VPS: # Modify the 'host' line of .tomo/config.rb host \"deployer@IPADDR\"","title":"Configure tomo"},{"location":"tutorials/deploying-rails-from-scratch/#run-tomo-setup","text":"Tomo comes with a setup command that will prepare your VPS for its first deployment. This does things like install Node, Yarn, and Ruby. It also sets up some important environment variables. Just run: # Run on your local machine $ tomo setup You will be asked to specify a value for the DATABASE_URL environment variable. Tomo will store this value on the VPS so that you only have to provide it once. Provide this value when prompted: sqlite3:/var/www/rails-new/shared/production.sqlite3 . Note that tomo setup compiles Ruby from source, which will take several minutes.","title":"Run tomo setup"},{"location":"tutorials/deploying-rails-from-scratch/#run-tomo-deploy","text":"Once setup completes, your VPS is ready to go. Deploying is now just a matter of running tomo deploy : # Run on your local machine $ tomo deploy When the deploy completes, the Rails app will be accessible on port 3000 of your VPS: http://IPADDR:3000 . Congratulations!","title":"Run tomo deploy"},{"location":"tutorials/publishing-a-plugin/","text":"Publishing a Plugin In this tutorial we will package a tomo plugin so that it can be shared with the community as a Ruby gem. Here is a quick reference to the steps involved in creating a plugin gem: Start with a project-based plugin to prove real-world usage Use the tomo-plugin GitHub template to package your plugin as a Ruby gem Write unit tests for your tasks using MockPluginTester Add docs and continuous integration (CI) to help your users and contributors Publish to rubygems.org To keep things simple, we\u2019ll package the example cron plugin that we created in the Writing Custom Tasks tutorial . Check out that article for more details on writing plugins and tasks. Here we will focus just on the gem packaging part. Create the gem While it is easy to get started writing tasks with a project-based plugin, this ad-hoc style does not have test coverage and is not packaged in a way that makes it easy to share with other projects and the larger tomo community. This is why it is useful to package your plugin in its own Ruby gem project. Choose a name Before packaging your plugin, choose a name. Your gem will be named tomo-plugin-NAME where NAME is the name of your plugin. Decide on a plugin name that is concise and whose gem name is not already taken at rubygems.org . For example the \u201csidekiq\u201d plugin corresponds to the tomo-plugin-sidekiq gem at rubygems.org. Use the tomo-plugin template Tomo provides a GitHub template that does most of the work of setting up the gem project. Navigate to the tomo-plugin template on GitHub Press Use this template Name the repo tomo-plugin-NAME where NAME is the name of the plugin For this example we will use \u201ccron\u201d as the plugin name. Run the rename_template.rb script Use git to clone the resulting GitHub repo. Inside the repo, run: $ ruby rename_template.rb This will prompt for some information needed to set up the project. Press enter to accept the default values, which should be sufficient for each question. >>>> git remote -v >>>> git remote get-url origin >>>> git config user.email >>>> git config user.name Plugin name? [cron] Gem summary (< 60 chars)? [cron tasks for tomo] Author email? [opensource@mattbrictson.com] Author name? [Matt Brictson] GitHub repository? [mattbrictson/tomo-plugin-cron] Create GitHub labels? [Y/n] I need to ask for your GitHub credentials in order to create labels. Don't worry, your GitHub credentials will NOT be saved. GitHub username? [mattbrictson] GitHub password? 2FA token? 012345 Created labels: \u26a0\ufe0f Breaking, \ud83d\udc1b Bug Fix, \ud83d\udcda Docs, \u2728 Feature, \ud83c\udfe0 Housekeeping >>>> git mv .github/workflows/push.yml.dist .github/workflows/push.yml >>>> git add LICENSE.txt >>>> git add Rakefile >>>> git add README.md >>>> git add CHANGELOG.md >>>> git add CODE_OF_CONDUCT.md >>>> git add bin/console >>>> git add example.gemspec >>>> git mv example.gemspec tomo-plugin-cron.gemspec >>>> git add lib/tomo/plugin/example.rb >>>> git mv lib/tomo/plugin/example.rb lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git add lib/tomo/plugin/example/helpers.rb >>>> git mv lib/tomo/plugin/example/helpers.rb lib/tomo/plugin/cron/helpers.rb >>>> git add lib/tomo/plugin/example/tasks.rb >>>> git mv lib/tomo/plugin/example/tasks.rb lib/tomo/plugin/cron/tasks.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git mv lib/tomo/plugin/example/version.rb lib/tomo/plugin/cron/version.rb >>>> git add lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/cron/version.rb >>>> git add test/tomo/plugin/example_test.rb >>>> git mv test/tomo/plugin/example_test.rb test/tomo/plugin/cron_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git mv test/tomo/plugin/example/helpers_test.rb test/tomo/plugin/cron/helpers_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git mv test/tomo/plugin/example/tasks_test.rb test/tomo/plugin/cron/tasks_test.rb >>>> git add test/test_helper.rb >>>> git rm rename_template.rb All set! The project has been renamed to \"tomo-plugin-cron\". Review the changes and then run: git commit && git push Don\u2019t forget to git commit and git push the changes. Define the plugin The entry point for a tomo plugin gem is the lib/tomo/plugin/.rb file. This file defines the plugin by extending Tomo::PluginDSL to provide the following three pieces of information: Default settings Tasks Helpers For our \u201ccron\u201d plugin, the entry point generated from the template looks like this: # lib/tomo/plugin/cron.rb require \"tomo\" require_relative \"cron/helpers\" require_relative \"cron/tasks\" require_relative \"cron/version\" module Tomo::Plugin::Cron extend Tomo::PluginDSL # TODO: initialize this plugin's settings with default values # defaults cron_setting: \"foo\", # cron_another_setting: \"bar\" tasks Tomo::Plugin::Cron::Tasks helpers Tomo::Plugin::Cron::Helpers end Default settings It is a good practice to provide default values for all settings that your plugin relies on. If there is truly no default value for a setting, declare it as nil . This at least informs tomo the name of the setting that the plugin is expecting. Our cron plugin needs a crontab template that has no default value (the user of the plugin will need to provide it): defaults cron_template_path: nil Notice how we\u2019ve named the setting starting with cron_ . This is a convention that makes it clear to users which settings correspond to which plugin, and reduces the unintended collisions. Tasks By default, the tasks of the plugin are defined in a file lib/tomo/plugin//tasks.rb . It looks like this: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary # tasks go here end end We can copy the tasks from the Writing Custom Tasks tutorial into this file with a few modifications to use the new :cron_template_path setting we just defined: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary def show remote.run \"crontab -l\", raise_on_error: false end def install require_setting :cron_template_path crontab = merge_template(settings[:cron_template_path]) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end end end Note the require_setting line will check that the user has provided a value for the :cron_template_path setting and will show a helpful error message if it is missing. Helpers Helpers extend the remote DSL in tomo. This is an advanced feature that most plugins will not use. We can remove it from our plugin: Delete lib/tomo/plugin/cron/helpers.rb Delete test/tomo/plugin/cron/helpers_test.rb Remove the following lines from lib/tomo/plugin/cron.rb require_relative \"cron/helpers\" helpers Tomo::Plugin::Cron::Helpers Write tests Tomo makes it easy to write tests in a plugin gem using the MockPluginTester . MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. Here\u2019s what tests look like for the tasks of our hypothetical cron plugin: # test/tomo/plugin/cron/tasks_test.rb require \"test_helper\" require \"tempfile\" class Tomo::Plugin::Cron::TasksTest < Minitest::Test def test_show tester = Tomo::Testing::MockPluginTester.new(\"cron\") tester.run_task(\"cron:show\") assert_equal(\"crontab -l\", tester.executed_script) end def test_install # Create a sample crontab template for use in the test crontab = \"0 6 * * * echo hi\\n\" crontab_file = Tempfile.new crontab_file << crontab crontab_file.close tester = Tomo::Testing::MockPluginTester.new( \"cron\", settings: { cron_template_path: crontab_file.path } ) tester.run_task(\"cron:install\") assert_equal( \"echo #{crontab.shellescape} | crontab -\", tester.executed_script ) end end To run the tests: $ bundle exec rake Started with run options --seed 29280 Tomo::Plugin::CronTest test_that_it_has_a_version_number PASS (0.00s) Tomo::Plugin::Cron::TasksTest test_install PASS (0.03s) test_show PASS (0.00s) Finished in 0.03056s 3 tests, 3 assertions, 0 failures, 0 errors, 0 skips Running RuboCop... Inspecting 12 files ............ 12 files inspected, no offenses detected Try it out Unit tests are valuable but no substitute for trying out your plugin gem in a real-world scenario. There are two easy ways to try your gem before publishing. One option is to install the gem into your local Ruby environment like this: $ bundle exec rake install Now your plugin will be available anywhere you use tomo on your local system. However, that won\u2019t work for a project where you are managing tomo gems via Bundler. In that case, edit the Gemfile of the project to reference your plugin by path : gem \"tomo-plugin-cron\", path: \"../tomo-plugin-cron\" Run bundle install and now your plugin will be available via bundle exec tomo . Write a good README The tomo-plugin template will create a basic README but it is up to you to fill in the details. In particular users will be interested to know what tasks your plugin provides and how to configure it. Make sure to fill in the Settings and Tasks sections of the README. In the case of our hypothetical cron plugin, we\u2019d want to specify installation instructions as well, to explain that the cron:install task should be listed part of the setup tasks. Check out tomo-plugin-sidekiq for an example README. Set up CI The tomo-plugin template will set your gem project up with Travis, Circle CI, and Code Climate out of the box, but it is up to you to log into those services and turn them on. Here are some links to get you started: Circle CI : Log in via your GitHub account and then click Add Projects . Code Climate : Log into the Quality product via your GitHub account, click Open Source , then Add a repository . Travis CI : Log into travis-ci.com via your GitHub account, go to Account Settings , then enable your gem project in the Repositories section. These services will build and test your project on every push and PR and update the status badges in your README. Publish to rubygems.org Once you are ready to share your gem with the world, create a rubygems.org account if you haven\u2019t already. Then ensure you are logged in via the gem CLI: $ gem signin To publish, run: $ bundle exec rake release Finally, don\u2019t forget to document your release with release notes in the Releases section of your project on GitHub. Releasing a new version If you\u2019ve already published your gem and want to release a new version, first edit the version.rb file: # lib/tomo/plugin/cron/version.rb module Tomo::Plugin::Cron VERSION = \"0.2.0\".freeze end Commit the changes: $ git commit -a -m \"Release v0.2.0\" And then release: $ bundle exec rake release Once again, don\u2019t forget the release notes on GitHub. That\u2019s it!","title":"Publishing a Plugin"},{"location":"tutorials/publishing-a-plugin/#publishing-a-plugin","text":"In this tutorial we will package a tomo plugin so that it can be shared with the community as a Ruby gem. Here is a quick reference to the steps involved in creating a plugin gem: Start with a project-based plugin to prove real-world usage Use the tomo-plugin GitHub template to package your plugin as a Ruby gem Write unit tests for your tasks using MockPluginTester Add docs and continuous integration (CI) to help your users and contributors Publish to rubygems.org To keep things simple, we\u2019ll package the example cron plugin that we created in the Writing Custom Tasks tutorial . Check out that article for more details on writing plugins and tasks. Here we will focus just on the gem packaging part.","title":"Publishing a Plugin"},{"location":"tutorials/publishing-a-plugin/#create-the-gem","text":"While it is easy to get started writing tasks with a project-based plugin, this ad-hoc style does not have test coverage and is not packaged in a way that makes it easy to share with other projects and the larger tomo community. This is why it is useful to package your plugin in its own Ruby gem project.","title":"Create the gem"},{"location":"tutorials/publishing-a-plugin/#choose-a-name","text":"Before packaging your plugin, choose a name. Your gem will be named tomo-plugin-NAME where NAME is the name of your plugin. Decide on a plugin name that is concise and whose gem name is not already taken at rubygems.org . For example the \u201csidekiq\u201d plugin corresponds to the tomo-plugin-sidekiq gem at rubygems.org.","title":"Choose a name"},{"location":"tutorials/publishing-a-plugin/#use-the-tomo-plugin-template","text":"Tomo provides a GitHub template that does most of the work of setting up the gem project. Navigate to the tomo-plugin template on GitHub Press Use this template Name the repo tomo-plugin-NAME where NAME is the name of the plugin For this example we will use \u201ccron\u201d as the plugin name.","title":"Use the tomo-plugin template"},{"location":"tutorials/publishing-a-plugin/#run-the-rename_templaterb-script","text":"Use git to clone the resulting GitHub repo. Inside the repo, run: $ ruby rename_template.rb This will prompt for some information needed to set up the project. Press enter to accept the default values, which should be sufficient for each question. >>>> git remote -v >>>> git remote get-url origin >>>> git config user.email >>>> git config user.name Plugin name? [cron] Gem summary (< 60 chars)? [cron tasks for tomo] Author email? [opensource@mattbrictson.com] Author name? [Matt Brictson] GitHub repository? [mattbrictson/tomo-plugin-cron] Create GitHub labels? [Y/n] I need to ask for your GitHub credentials in order to create labels. Don't worry, your GitHub credentials will NOT be saved. GitHub username? [mattbrictson] GitHub password? 2FA token? 012345 Created labels: \u26a0\ufe0f Breaking, \ud83d\udc1b Bug Fix, \ud83d\udcda Docs, \u2728 Feature, \ud83c\udfe0 Housekeeping >>>> git mv .github/workflows/push.yml.dist .github/workflows/push.yml >>>> git add LICENSE.txt >>>> git add Rakefile >>>> git add README.md >>>> git add CHANGELOG.md >>>> git add CODE_OF_CONDUCT.md >>>> git add bin/console >>>> git add example.gemspec >>>> git mv example.gemspec tomo-plugin-cron.gemspec >>>> git add lib/tomo/plugin/example.rb >>>> git mv lib/tomo/plugin/example.rb lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git add lib/tomo/plugin/example/helpers.rb >>>> git mv lib/tomo/plugin/example/helpers.rb lib/tomo/plugin/cron/helpers.rb >>>> git add lib/tomo/plugin/example/tasks.rb >>>> git mv lib/tomo/plugin/example/tasks.rb lib/tomo/plugin/cron/tasks.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git mv lib/tomo/plugin/example/version.rb lib/tomo/plugin/cron/version.rb >>>> git add lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/cron/version.rb >>>> git add test/tomo/plugin/example_test.rb >>>> git mv test/tomo/plugin/example_test.rb test/tomo/plugin/cron_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git mv test/tomo/plugin/example/helpers_test.rb test/tomo/plugin/cron/helpers_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git mv test/tomo/plugin/example/tasks_test.rb test/tomo/plugin/cron/tasks_test.rb >>>> git add test/test_helper.rb >>>> git rm rename_template.rb All set! The project has been renamed to \"tomo-plugin-cron\". Review the changes and then run: git commit && git push Don\u2019t forget to git commit and git push the changes.","title":"Run the rename_template.rb script"},{"location":"tutorials/publishing-a-plugin/#define-the-plugin","text":"The entry point for a tomo plugin gem is the lib/tomo/plugin/.rb file. This file defines the plugin by extending Tomo::PluginDSL to provide the following three pieces of information: Default settings Tasks Helpers For our \u201ccron\u201d plugin, the entry point generated from the template looks like this: # lib/tomo/plugin/cron.rb require \"tomo\" require_relative \"cron/helpers\" require_relative \"cron/tasks\" require_relative \"cron/version\" module Tomo::Plugin::Cron extend Tomo::PluginDSL # TODO: initialize this plugin's settings with default values # defaults cron_setting: \"foo\", # cron_another_setting: \"bar\" tasks Tomo::Plugin::Cron::Tasks helpers Tomo::Plugin::Cron::Helpers end","title":"Define the plugin"},{"location":"tutorials/publishing-a-plugin/#default-settings","text":"It is a good practice to provide default values for all settings that your plugin relies on. If there is truly no default value for a setting, declare it as nil . This at least informs tomo the name of the setting that the plugin is expecting. Our cron plugin needs a crontab template that has no default value (the user of the plugin will need to provide it): defaults cron_template_path: nil Notice how we\u2019ve named the setting starting with cron_ . This is a convention that makes it clear to users which settings correspond to which plugin, and reduces the unintended collisions.","title":"Default settings"},{"location":"tutorials/publishing-a-plugin/#tasks","text":"By default, the tasks of the plugin are defined in a file lib/tomo/plugin//tasks.rb . It looks like this: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary # tasks go here end end We can copy the tasks from the Writing Custom Tasks tutorial into this file with a few modifications to use the new :cron_template_path setting we just defined: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary def show remote.run \"crontab -l\", raise_on_error: false end def install require_setting :cron_template_path crontab = merge_template(settings[:cron_template_path]) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end end end Note the require_setting line will check that the user has provided a value for the :cron_template_path setting and will show a helpful error message if it is missing.","title":"Tasks"},{"location":"tutorials/publishing-a-plugin/#helpers","text":"Helpers extend the remote DSL in tomo. This is an advanced feature that most plugins will not use. We can remove it from our plugin: Delete lib/tomo/plugin/cron/helpers.rb Delete test/tomo/plugin/cron/helpers_test.rb Remove the following lines from lib/tomo/plugin/cron.rb require_relative \"cron/helpers\" helpers Tomo::Plugin::Cron::Helpers","title":"Helpers"},{"location":"tutorials/publishing-a-plugin/#write-tests","text":"Tomo makes it easy to write tests in a plugin gem using the MockPluginTester . MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. Here\u2019s what tests look like for the tasks of our hypothetical cron plugin: # test/tomo/plugin/cron/tasks_test.rb require \"test_helper\" require \"tempfile\" class Tomo::Plugin::Cron::TasksTest < Minitest::Test def test_show tester = Tomo::Testing::MockPluginTester.new(\"cron\") tester.run_task(\"cron:show\") assert_equal(\"crontab -l\", tester.executed_script) end def test_install # Create a sample crontab template for use in the test crontab = \"0 6 * * * echo hi\\n\" crontab_file = Tempfile.new crontab_file << crontab crontab_file.close tester = Tomo::Testing::MockPluginTester.new( \"cron\", settings: { cron_template_path: crontab_file.path } ) tester.run_task(\"cron:install\") assert_equal( \"echo #{crontab.shellescape} | crontab -\", tester.executed_script ) end end To run the tests: $ bundle exec rake Started with run options --seed 29280 Tomo::Plugin::CronTest test_that_it_has_a_version_number PASS (0.00s) Tomo::Plugin::Cron::TasksTest test_install PASS (0.03s) test_show PASS (0.00s) Finished in 0.03056s 3 tests, 3 assertions, 0 failures, 0 errors, 0 skips Running RuboCop... Inspecting 12 files ............ 12 files inspected, no offenses detected","title":"Write tests"},{"location":"tutorials/publishing-a-plugin/#try-it-out","text":"Unit tests are valuable but no substitute for trying out your plugin gem in a real-world scenario. There are two easy ways to try your gem before publishing. One option is to install the gem into your local Ruby environment like this: $ bundle exec rake install Now your plugin will be available anywhere you use tomo on your local system. However, that won\u2019t work for a project where you are managing tomo gems via Bundler. In that case, edit the Gemfile of the project to reference your plugin by path : gem \"tomo-plugin-cron\", path: \"../tomo-plugin-cron\" Run bundle install and now your plugin will be available via bundle exec tomo .","title":"Try it out"},{"location":"tutorials/publishing-a-plugin/#write-a-good-readme","text":"The tomo-plugin template will create a basic README but it is up to you to fill in the details. In particular users will be interested to know what tasks your plugin provides and how to configure it. Make sure to fill in the Settings and Tasks sections of the README. In the case of our hypothetical cron plugin, we\u2019d want to specify installation instructions as well, to explain that the cron:install task should be listed part of the setup tasks. Check out tomo-plugin-sidekiq for an example README.","title":"Write a good README"},{"location":"tutorials/publishing-a-plugin/#set-up-ci","text":"The tomo-plugin template will set your gem project up with Travis, Circle CI, and Code Climate out of the box, but it is up to you to log into those services and turn them on. Here are some links to get you started: Circle CI : Log in via your GitHub account and then click Add Projects . Code Climate : Log into the Quality product via your GitHub account, click Open Source , then Add a repository . Travis CI : Log into travis-ci.com via your GitHub account, go to Account Settings , then enable your gem project in the Repositories section. These services will build and test your project on every push and PR and update the status badges in your README.","title":"Set up CI"},{"location":"tutorials/publishing-a-plugin/#publish-to-rubygemsorg","text":"Once you are ready to share your gem with the world, create a rubygems.org account if you haven\u2019t already. Then ensure you are logged in via the gem CLI: $ gem signin To publish, run: $ bundle exec rake release Finally, don\u2019t forget to document your release with release notes in the Releases section of your project on GitHub.","title":"Publish to rubygems.org"},{"location":"tutorials/publishing-a-plugin/#releasing-a-new-version","text":"If you\u2019ve already published your gem and want to release a new version, first edit the version.rb file: # lib/tomo/plugin/cron/version.rb module Tomo::Plugin::Cron VERSION = \"0.2.0\".freeze end Commit the changes: $ git commit -a -m \"Release v0.2.0\" And then release: $ bundle exec rake release Once again, don\u2019t forget the release notes on GitHub. That\u2019s it!","title":"Releasing a new version"},{"location":"tutorials/writing-custom-tasks/","text":"Writing Custom Tasks In this tutorial we will build a \u201ccron\u201d plugin to demonstrate how to write custom tasks in tomo. Here are the main takeaways: Use .tomo/plugins/*.rb to define plugins A task is any public Ruby method within a plugin Tasks can access the TaskLibrary API Use remote.run to execute scripts on the remote host Here\u2019s the final product: # .tomo/config.rb plugin \"./plugins/cron.rb\" # .tomo/plugins/cron.rb def show remote.run \"crontab -l\", raise_on_error: false end def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Before we get there, let\u2019s review the basics. What is a plugin? Plugins extend tomo by providing some or all of these three things: tasks, helpers, and default settings. Plugins are either built into tomo (e.g. git , rails ), provided by gems (e.g. tomo-plugin-sidekiq ), or loaded from ./tomo/plugins/*.rb within a tomo project. This tutorial will focus on project-specific plugins, which are the easiest to write. Once you are ready to share your plugin amongst multiple projects (or with the larger tomo community), check out the Publishing a Plugin tutorial to learn how to package a plugin as a gem. What is a task? In tomo, a task is a plain Ruby method provided by a plugin. Task methods take zero arguments. Here is a trivial example: # .tomo/plugins/foo.rb # This defines a foo:hello task def hello logger.info \"hello, world!\" end The name of the plugin is set automatically based on the name of the .rb file, which in this case is \u201cfoo\u201d. Any public method defined in foo.rb becomes a tomo task. So the example above defines a foo:hello task that prints \u201chello, world!\u201d to the console. What can a task do? Behind the scenes, the Ruby methods you define in your plugin are actually methods on a subclass of TaskLibrary . That means you have full access to the TaskLibrary API within your task method, which includes: logger for printing output (as seen in the example above) remote for running scripts on the remote host settings for accessing project configuration paths for convenient access to filesystem paths on the remote host and more\u2026 For example, a simplified, annotated version of the git:clone task that is built into tomo looks like this: def clone # Halt tomo with an error message if the :git_url setting is nil/unspecified require_setting :git_url # Run \"mkdir -p\" on the remote host to create the parent directory # of the :git_repo_path setting remote.mkdir_p(paths.git_repo.dirname) # Run \"git clone ...\" on the remote host to clone the repo into :git_repo_path remote.run(\"git\", \"clone\", \"--mirror\", settings[:git_url], paths.git_repo) end When are tasks run? Tomo does not have hooks and tasks cannot invoke other tasks. That means that tasks only run when explicitly requested by the user. There are three ways to run a task: deploy setup run For deploy and setup, users invoke your task by including it in the deploy or setup list of tasks in .tomo/config.rb . Additionally, any task can be run on-demand from the command line, like this: $ tomo run foo:hello In the command line case, users can optionally pass arguments to a task. These arguments become available to the task via the :run_args setting. For example, the rails:console task supports command line arguments like this: def console # If this task is run like `tomo run -- rails:console --sandbox` # then settings[:run_args] will be [\"--sandbox\"] args = settings[:run_args] remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"rails\", \"console\", *args, attach: true) end end How do tasks connect to remote hosts? Notice that none of the examples in this tutorial makes any mention of opening/closing connections or specifying hosts or roles. That is because tomo takes care of connecting to remote hosts and automatically decides which tasks should run on which hosts based on project configuration. By the time a task method is invoked, any necessary SSH connection is already established; remote implicitly refers to that connection. In other words, as a tomo task author you only need to be concerned about what remote scripts to run, not where or how they are executed. For a more in-depth explanation of how configuration drives tomo\u2019s behavior, refer to the configuration docs . Tutorial Let\u2019s build something using this knowledge of how tomo tasks work. Objective Say we have a Rails app that needs to run code \u2013 PeriodicTask.call , for example \u2013 every day at 06:00. We\u2019d like to do this with a cron job and use tomo to install that cron job on the remote host. For troubleshooting purposes it would be nice to view the list of cron jobs with tomo as well. That sounds like two distinct tomo tasks: cron:install to install the cron job cron:show to list the currently installed cron jobs We want cron:install to be run when we initially set up the remote host. In other words, it should run as part of tomo setup . On the other hand, cron:show is a utility that we can use on the CLI when needed. cron:show We\u2019ll start by building the simpler of the two tasks: cron:show . First, let\u2019s try to run that task: $ tomo run cron:show tomo run v1.0.0 ERROR: cron:show is not a recognized task. To see a list of all available tasks, run tomo tasks. We haven\u2019t written the task yet, so this error makes sense. Let\u2019s build a skeleton of the cron:show task to fix this error. Create a .tomo/plugins/cron.rb task like this: # .tomo/plugins/cron.rb def show logger.info \"Hi\" end And don\u2019t forget to load the plugin in .tomo/config.rb : # .tomo/config.rb plugin \"./plugins/cron.rb\" Now we can try again: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show Hi \u2714 Ran cron:show on deployer@app.example.com Great! To get a list of cron jobs, we need to run crontab -l on the remote host: def show remote.run \"crontab -l\" end One more try: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer ERROR: The following script failed on deployer@app.example.com (exit status 1). crontab -l You can manually re-execute the script via SSH as follows: ssh -o LogLevel\\=ERROR -A -o ConnectTimeout\\=5 -o StrictHostKeyChecking\\=accept-new -o ControlMaster\\=auto -o ControlPath\\=/var/folders/_v/j_5kgc6n1nz5pb7kfkzz3r5c0000gn/T/tomo_ssh_1f061db77f81ae9e -o ControlPersist\\=30s -o PasswordAuthentication\\=no deployer@app.example.com -- crontab\\ -l For more troubleshooting info, run tomo again using the --debug option. no crontab for deployer Uh oh. There are no cron jobs installed yet, so crontab -l exits with an error. By default, tomo assumes that any remote command the exits with an error status is considered fatal. In this case we just want to see the error output from the crontab command and continue without complaint; that\u2019s where the raise_on_error: false option comes into play: def show remote.run \"crontab -l\", raise_on_error: false end Now we\u2019re all good: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer \u2714 Ran cron:show on deployer@app.example.com cron:install Before we said that we want a cron:install task that runs as part of tomo setup . Let\u2019s start by adding that task to the list of setup tasks in .tomo/config.rb : # .tomo/config.rb setup do # ... other tasks omitted for brevity run \"cron:install\" end If we try to run tomo setup at this point, we\u2019ll get an error as expected: $ tomo setup tomo setup v1.0.0 ERROR: cron:install is not a recognized task. To see a list of all available tasks, run tomo tasks. Did you mean rbenv:install? Cron jobs can be installed by piping a list of cron definitions to crontab - (the - means to read the definitions from stdin). We can take advantage of this to write a simple cron:install task: def install crontab = <<~CRONTAB SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /var/www/my-app/current; bundle exec rails runner PeriodicTask.call > /var/www/my-app/shared/log/periodic-task.log 2>&1 CRONTAB remote.run \"echo #{crontab.shellescape} | crontab -\" end Note that we are using shellescape as part of Ruby\u2019s built-in shellwords library to safely build the script. We can see what this task does without actually affecting the remote host by using --dry-run option: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Looks good! But we if we made it more powerful with some ERB templating? Templates Tomo offers a convenient way to use ERB templates with it\u2019s built-in merge_template and write methods. We can use merge_template instead of a hard-coding the cron job: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\" end The ERB template has access to all the same APIs as our task methods; that means we can remove the hard-coded paths from our original cron job specification and use tomo\u2019s paths helper. So our ERB template file ( .tomo/templates/crontab.erb ) could look like this: # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Let\u2019s check that it still works: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) That\u2019s great, but the output is really verbose. Do we really need to see the full contents of the crontab being echoed? What if our template becomes really large? In tomo, you can mute this output using echo: false , but you can also provide an echo string to show instead of the command. We can use this to echo an abbreviated version: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end And then try it: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo [template:.tomo/templates/crontab.erb] | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Ah, much cleaner! The result We now have a cron:install task that will automatically run as part of tomo setup , or can be run manually using tomo run cron:install . Let\u2019s try it for real: $ tomo setup ... [snip] ... \u2022 cron:install echo [template:.tomo/templates/crontab.erb] | crontab - \u2714 Performed setup of my-app on deployer@app.example.com And we can see what is installed with our cron:show task: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /home/deployer/apps/my-app/current; bundle exec rails runner PeriodicTask.call > /home/deployer/apps/my-app/shared/log/periodic-task.log 2>&1 \u2714 Ran cron:show on deployer@app.example.com Next steps This tutorial introduced you to writing custom tasks in tomo, but there is much more to explore. For next steps, check out these APIs: Tomo::TaskLibrary Tomo::Remote Tomo::Result Tomo::Paths core plugin helpers (additional methods mixed into the Remote API) And for inspiration, look no further than tomo itself, which has several built-in plugins in lib/tomo/plugin .","title":"Writing Custom Tasks"},{"location":"tutorials/writing-custom-tasks/#writing-custom-tasks","text":"In this tutorial we will build a \u201ccron\u201d plugin to demonstrate how to write custom tasks in tomo. Here are the main takeaways: Use .tomo/plugins/*.rb to define plugins A task is any public Ruby method within a plugin Tasks can access the TaskLibrary API Use remote.run to execute scripts on the remote host Here\u2019s the final product: # .tomo/config.rb plugin \"./plugins/cron.rb\" # .tomo/plugins/cron.rb def show remote.run \"crontab -l\", raise_on_error: false end def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Before we get there, let\u2019s review the basics.","title":"Writing Custom Tasks"},{"location":"tutorials/writing-custom-tasks/#what-is-a-plugin","text":"Plugins extend tomo by providing some or all of these three things: tasks, helpers, and default settings. Plugins are either built into tomo (e.g. git , rails ), provided by gems (e.g. tomo-plugin-sidekiq ), or loaded from ./tomo/plugins/*.rb within a tomo project. This tutorial will focus on project-specific plugins, which are the easiest to write. Once you are ready to share your plugin amongst multiple projects (or with the larger tomo community), check out the Publishing a Plugin tutorial to learn how to package a plugin as a gem.","title":"What is a plugin?"},{"location":"tutorials/writing-custom-tasks/#what-is-a-task","text":"In tomo, a task is a plain Ruby method provided by a plugin. Task methods take zero arguments. Here is a trivial example: # .tomo/plugins/foo.rb # This defines a foo:hello task def hello logger.info \"hello, world!\" end The name of the plugin is set automatically based on the name of the .rb file, which in this case is \u201cfoo\u201d. Any public method defined in foo.rb becomes a tomo task. So the example above defines a foo:hello task that prints \u201chello, world!\u201d to the console.","title":"What is a task?"},{"location":"tutorials/writing-custom-tasks/#what-can-a-task-do","text":"Behind the scenes, the Ruby methods you define in your plugin are actually methods on a subclass of TaskLibrary . That means you have full access to the TaskLibrary API within your task method, which includes: logger for printing output (as seen in the example above) remote for running scripts on the remote host settings for accessing project configuration paths for convenient access to filesystem paths on the remote host and more\u2026 For example, a simplified, annotated version of the git:clone task that is built into tomo looks like this: def clone # Halt tomo with an error message if the :git_url setting is nil/unspecified require_setting :git_url # Run \"mkdir -p\" on the remote host to create the parent directory # of the :git_repo_path setting remote.mkdir_p(paths.git_repo.dirname) # Run \"git clone ...\" on the remote host to clone the repo into :git_repo_path remote.run(\"git\", \"clone\", \"--mirror\", settings[:git_url], paths.git_repo) end","title":"What can a task do?"},{"location":"tutorials/writing-custom-tasks/#when-are-tasks-run","text":"Tomo does not have hooks and tasks cannot invoke other tasks. That means that tasks only run when explicitly requested by the user. There are three ways to run a task: deploy setup run For deploy and setup, users invoke your task by including it in the deploy or setup list of tasks in .tomo/config.rb . Additionally, any task can be run on-demand from the command line, like this: $ tomo run foo:hello In the command line case, users can optionally pass arguments to a task. These arguments become available to the task via the :run_args setting. For example, the rails:console task supports command line arguments like this: def console # If this task is run like `tomo run -- rails:console --sandbox` # then settings[:run_args] will be [\"--sandbox\"] args = settings[:run_args] remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"rails\", \"console\", *args, attach: true) end end","title":"When are tasks run?"},{"location":"tutorials/writing-custom-tasks/#how-do-tasks-connect-to-remote-hosts","text":"Notice that none of the examples in this tutorial makes any mention of opening/closing connections or specifying hosts or roles. That is because tomo takes care of connecting to remote hosts and automatically decides which tasks should run on which hosts based on project configuration. By the time a task method is invoked, any necessary SSH connection is already established; remote implicitly refers to that connection. In other words, as a tomo task author you only need to be concerned about what remote scripts to run, not where or how they are executed. For a more in-depth explanation of how configuration drives tomo\u2019s behavior, refer to the configuration docs .","title":"How do tasks connect to remote hosts?"},{"location":"tutorials/writing-custom-tasks/#tutorial","text":"Let\u2019s build something using this knowledge of how tomo tasks work.","title":"Tutorial"},{"location":"tutorials/writing-custom-tasks/#objective","text":"Say we have a Rails app that needs to run code \u2013 PeriodicTask.call , for example \u2013 every day at 06:00. We\u2019d like to do this with a cron job and use tomo to install that cron job on the remote host. For troubleshooting purposes it would be nice to view the list of cron jobs with tomo as well. That sounds like two distinct tomo tasks: cron:install to install the cron job cron:show to list the currently installed cron jobs We want cron:install to be run when we initially set up the remote host. In other words, it should run as part of tomo setup . On the other hand, cron:show is a utility that we can use on the CLI when needed.","title":"Objective"},{"location":"tutorials/writing-custom-tasks/#cronshow","text":"We\u2019ll start by building the simpler of the two tasks: cron:show . First, let\u2019s try to run that task: $ tomo run cron:show tomo run v1.0.0 ERROR: cron:show is not a recognized task. To see a list of all available tasks, run tomo tasks. We haven\u2019t written the task yet, so this error makes sense. Let\u2019s build a skeleton of the cron:show task to fix this error. Create a .tomo/plugins/cron.rb task like this: # .tomo/plugins/cron.rb def show logger.info \"Hi\" end And don\u2019t forget to load the plugin in .tomo/config.rb : # .tomo/config.rb plugin \"./plugins/cron.rb\" Now we can try again: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show Hi \u2714 Ran cron:show on deployer@app.example.com Great! To get a list of cron jobs, we need to run crontab -l on the remote host: def show remote.run \"crontab -l\" end One more try: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer ERROR: The following script failed on deployer@app.example.com (exit status 1). crontab -l You can manually re-execute the script via SSH as follows: ssh -o LogLevel\\=ERROR -A -o ConnectTimeout\\=5 -o StrictHostKeyChecking\\=accept-new -o ControlMaster\\=auto -o ControlPath\\=/var/folders/_v/j_5kgc6n1nz5pb7kfkzz3r5c0000gn/T/tomo_ssh_1f061db77f81ae9e -o ControlPersist\\=30s -o PasswordAuthentication\\=no deployer@app.example.com -- crontab\\ -l For more troubleshooting info, run tomo again using the --debug option. no crontab for deployer Uh oh. There are no cron jobs installed yet, so crontab -l exits with an error. By default, tomo assumes that any remote command the exits with an error status is considered fatal. In this case we just want to see the error output from the crontab command and continue without complaint; that\u2019s where the raise_on_error: false option comes into play: def show remote.run \"crontab -l\", raise_on_error: false end Now we\u2019re all good: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer \u2714 Ran cron:show on deployer@app.example.com","title":"cron:show"},{"location":"tutorials/writing-custom-tasks/#croninstall","text":"Before we said that we want a cron:install task that runs as part of tomo setup . Let\u2019s start by adding that task to the list of setup tasks in .tomo/config.rb : # .tomo/config.rb setup do # ... other tasks omitted for brevity run \"cron:install\" end If we try to run tomo setup at this point, we\u2019ll get an error as expected: $ tomo setup tomo setup v1.0.0 ERROR: cron:install is not a recognized task. To see a list of all available tasks, run tomo tasks. Did you mean rbenv:install? Cron jobs can be installed by piping a list of cron definitions to crontab - (the - means to read the definitions from stdin). We can take advantage of this to write a simple cron:install task: def install crontab = <<~CRONTAB SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /var/www/my-app/current; bundle exec rails runner PeriodicTask.call > /var/www/my-app/shared/log/periodic-task.log 2>&1 CRONTAB remote.run \"echo #{crontab.shellescape} | crontab -\" end Note that we are using shellescape as part of Ruby\u2019s built-in shellwords library to safely build the script. We can see what this task does without actually affecting the remote host by using --dry-run option: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Looks good! But we if we made it more powerful with some ERB templating?","title":"cron:install"},{"location":"tutorials/writing-custom-tasks/#templates","text":"Tomo offers a convenient way to use ERB templates with it\u2019s built-in merge_template and write methods. We can use merge_template instead of a hard-coding the cron job: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\" end The ERB template has access to all the same APIs as our task methods; that means we can remove the hard-coded paths from our original cron job specification and use tomo\u2019s paths helper. So our ERB template file ( .tomo/templates/crontab.erb ) could look like this: # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Let\u2019s check that it still works: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) That\u2019s great, but the output is really verbose. Do we really need to see the full contents of the crontab being echoed? What if our template becomes really large? In tomo, you can mute this output using echo: false , but you can also provide an echo string to show instead of the command. We can use this to echo an abbreviated version: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end And then try it: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo [template:.tomo/templates/crontab.erb] | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Ah, much cleaner!","title":"Templates"},{"location":"tutorials/writing-custom-tasks/#the-result","text":"We now have a cron:install task that will automatically run as part of tomo setup , or can be run manually using tomo run cron:install . Let\u2019s try it for real: $ tomo setup ... [snip] ... \u2022 cron:install echo [template:.tomo/templates/crontab.erb] | crontab - \u2714 Performed setup of my-app on deployer@app.example.com And we can see what is installed with our cron:show task: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /home/deployer/apps/my-app/current; bundle exec rails runner PeriodicTask.call > /home/deployer/apps/my-app/shared/log/periodic-task.log 2>&1 \u2714 Ran cron:show on deployer@app.example.com","title":"The result"},{"location":"tutorials/writing-custom-tasks/#next-steps","text":"This tutorial introduced you to writing custom tasks in tomo, but there is much more to explore. For next steps, check out these APIs: Tomo::TaskLibrary Tomo::Remote Tomo::Result Tomo::Paths core plugin helpers (additional methods mixed into the Remote API) And for inspiration, look no further than tomo itself, which has several built-in plugins in lib/tomo/plugin .","title":"Next steps"}]} \ No newline at end of file +{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Tomo Tomo is a friendly command-line tool for deploying Rails apps. \ud83d\udcbb Rich command-line interface with built-in bash completions \u2601\ufe0f Multi-environment and role-based multi-host support \ud83d\udc8e Everything you need to deploy a basic Rails app out of the box \ud83d\udd0c Easily extensible for polyglot projects (not just Rails!) \ud83d\udcda Quality documentation \ud83d\udd2c Minimal dependencies \u2192 See how tomo compares to other Ruby deployment tools like Capistrano and Mina. Quick start Usage Extending tomo Tutorials Blog posts Reference documentation FAQ Support License Code of conduct Contribution guide Quick start Installation Tomo is distributed as a ruby gem. To install: $ gem install tomo \ud83d\udca1 Protip: run tomo completion-script for instructions on setting up bash completions. Configuring a project Tomo is configured via a .tomo/config.rb file in your project. To get started, run tomo init to generate a configuration that works for a basic Rails app. An abbreviated version looks like this: # .tomo/config.rb plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"user@hostname.or.ip.address\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end Next steps \u2192 The reference docs have a complete guide to tomo configuration. \u2192 Check out the Deploying Rails From Scratch tutorial for a step-by-step guide to using tomo with a real app. Usage Once your project is configured, you can: Run tomo setup to prepare the remote host for its first deploy. Run tomo deploy to deploy your app. Use tomo run to invoke one-off tasks, like launching a Rails console. \ud83d\udca1 Protip: add -h or --help when running any of these commands to see detailed docs and examples. tomo setup tomo setup prepares the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. Out of the box, tomo will: Configure necessary environment variables, like RAILS_ENV and SECRET_KEY_BASE Install Ruby, Bundler, Node, Yarn, and dependencies Create all necessary deployment directories Create the Rails database, load the schema, and insert seed data \u2192 Here is the default list of tasks invoked by the setup command. \u2192 The tomo setup section of the reference docs explains supported command-line options. tomo deploy Whereas tomo setup is typically run once, you can use tomo deploy every time you want to deploy a new version of your app. The deploy command will sequentially run the deploy list of tasks specified in .tomo/config.rb . You can customize this list to meet the needs of your app. By default, tomo runs these tasks: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) \ud83d\udca1 Protip: you can abbreviate tomo commands, like tomo d for tomo deploy or tomo s for tomo setup . \u2192 Here is the default list of tasks invoked by the deploy command. \u2192 The tomo deploy section of the reference docs explains supported command-line options, like --dry-run . tomo run [TASK] Tomo can also run individual remote tasks on demand. You can use the tasks command to see the list of tasks tomo knows about. One of the built-in Rails tasks is rails:console , which brings up a fully-interactive Rails console over SSH. \ud83d\udca1 Protip: you can shorten this as tomo rails:console (the run command is implied). \u2192 The tomo run section of the reference docs explains supported command-line options and has more examples. Extending tomo Tomo has a powerful plugin system that lets you extend tomo by installing Ruby gems (e.g. tomo-plugin-sidekiq ). You can also define plugins on the fly within your project by adding simple .rb files to .tomo/plugins/ . These plugins can define tasks as plain ruby methods. For example: # .tomo/plugins/my-plugin.rb def hello remote.run \"echo\", \"hello\", settings[:application] end Load your plugin in config.rb like this: # .tomo/config.rb plugin \"./plugins/my-plugin.rb\" And run it! \u2192 The Writing Custom Tasks tutorial has an in-depth explanation of how plugins work. \u2192 The TaskLibrary API is tomo\u2019s DSL for building tasks. \u2192 The Publishing a Plugin tutorial explains how to package your plugin as a Ruby gem to share it with the community. Tutorials Deploying Rails From Scratch Writing Custom Tasks Publishing a Plugin Blog posts Automate Rails deployments with GitHub Actions Reference documentation Configuration Commands init setup deploy run tasks Plugins core bundler env git nodenv puma rails rbenv API Host Logger Paths PluginDSL Remote Result TaskLibrary Testing::MockPluginTester FAQ What does the unsupported option \"accept-new\" error mean? By default, tomo uses the \u201caccept-new\u201d value for the StrictHostKeyChecking option, which is supported by OpenSSH 7.6 and newer. If you are using an older version, this will cause an error. As a workaround, you can override tomo\u2019s default behavior like this: # Replace \"accept-new\" with something compatible with older versions of SSH set ssh_strict_host_key_checking: true # or false Can I deploy multiple apps to a single host? Tomo relies on the host user\u2019s bash profile for various things, like setting environment variables and initializing rbenv and nodenv. This makes it impractical to deploy multiple apps to a single host using the same deploy user. The solution is to create multiple users on the remote host, and then configure a different user for deploying each app. That way each user can have its own distinct environment variables and you can easily configure each app differently without risking conflicts. Refer to the tomo Rails tutorial for instructions on creating a deploy user. E.g. app1 would be configured to deploy as: host \"app1@example.com\" And app2 would be configured to deploy as: host \"app2@example.com\" Next run tomo setup for both apps; this will set everything up for both users on the remote host (environment variables, rbenv, etc.). You can now deploy both apps to the same host, with the confidence that their configurations will be kept cleanly separated. Does tomo support git submodules? No, not out of the box. However, you can extend tomo with an additional task for submodules; see the solution in PR #220 suggested by @numbcoder . Support This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you\u2019d like to help by submitting a pull request, or if you\u2019ve discovered a bug that needs my attention, please let me know. Check out CONTRIBUTING.md to get started. Happy hacking! \u2014Matt License The gem is available as open source under the terms of the MIT License . Code of conduct Everyone interacting in the Tomo project\u2019s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct . Contribution guide Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short CONTRIBUTING.md guidelines before you dive in.","title":"Home"},{"location":"#tomo","text":"Tomo is a friendly command-line tool for deploying Rails apps. \ud83d\udcbb Rich command-line interface with built-in bash completions \u2601\ufe0f Multi-environment and role-based multi-host support \ud83d\udc8e Everything you need to deploy a basic Rails app out of the box \ud83d\udd0c Easily extensible for polyglot projects (not just Rails!) \ud83d\udcda Quality documentation \ud83d\udd2c Minimal dependencies \u2192 See how tomo compares to other Ruby deployment tools like Capistrano and Mina. Quick start Usage Extending tomo Tutorials Blog posts Reference documentation FAQ Support License Code of conduct Contribution guide","title":"Tomo"},{"location":"#quick-start","text":"","title":"Quick start"},{"location":"#installation","text":"Tomo is distributed as a ruby gem. To install: $ gem install tomo \ud83d\udca1 Protip: run tomo completion-script for instructions on setting up bash completions.","title":"Installation"},{"location":"#configuring-a-project","text":"Tomo is configured via a .tomo/config.rb file in your project. To get started, run tomo init to generate a configuration that works for a basic Rails app. An abbreviated version looks like this: # .tomo/config.rb plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"user@hostname.or.ip.address\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end","title":"Configuring a project"},{"location":"#next-steps","text":"\u2192 The reference docs have a complete guide to tomo configuration. \u2192 Check out the Deploying Rails From Scratch tutorial for a step-by-step guide to using tomo with a real app.","title":"Next steps"},{"location":"#usage","text":"Once your project is configured, you can: Run tomo setup to prepare the remote host for its first deploy. Run tomo deploy to deploy your app. Use tomo run to invoke one-off tasks, like launching a Rails console. \ud83d\udca1 Protip: add -h or --help when running any of these commands to see detailed docs and examples.","title":"Usage"},{"location":"#tomo-setup","text":"tomo setup prepares the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. Out of the box, tomo will: Configure necessary environment variables, like RAILS_ENV and SECRET_KEY_BASE Install Ruby, Bundler, Node, Yarn, and dependencies Create all necessary deployment directories Create the Rails database, load the schema, and insert seed data \u2192 Here is the default list of tasks invoked by the setup command. \u2192 The tomo setup section of the reference docs explains supported command-line options.","title":"tomo setup"},{"location":"#tomo-deploy","text":"Whereas tomo setup is typically run once, you can use tomo deploy every time you want to deploy a new version of your app. The deploy command will sequentially run the deploy list of tasks specified in .tomo/config.rb . You can customize this list to meet the needs of your app. By default, tomo runs these tasks: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) \ud83d\udca1 Protip: you can abbreviate tomo commands, like tomo d for tomo deploy or tomo s for tomo setup . \u2192 Here is the default list of tasks invoked by the deploy command. \u2192 The tomo deploy section of the reference docs explains supported command-line options, like --dry-run .","title":"tomo deploy"},{"location":"#tomo-run-task","text":"Tomo can also run individual remote tasks on demand. You can use the tasks command to see the list of tasks tomo knows about. One of the built-in Rails tasks is rails:console , which brings up a fully-interactive Rails console over SSH. \ud83d\udca1 Protip: you can shorten this as tomo rails:console (the run command is implied). \u2192 The tomo run section of the reference docs explains supported command-line options and has more examples.","title":"tomo run [TASK]"},{"location":"#extending-tomo","text":"Tomo has a powerful plugin system that lets you extend tomo by installing Ruby gems (e.g. tomo-plugin-sidekiq ). You can also define plugins on the fly within your project by adding simple .rb files to .tomo/plugins/ . These plugins can define tasks as plain ruby methods. For example: # .tomo/plugins/my-plugin.rb def hello remote.run \"echo\", \"hello\", settings[:application] end Load your plugin in config.rb like this: # .tomo/config.rb plugin \"./plugins/my-plugin.rb\" And run it! \u2192 The Writing Custom Tasks tutorial has an in-depth explanation of how plugins work. \u2192 The TaskLibrary API is tomo\u2019s DSL for building tasks. \u2192 The Publishing a Plugin tutorial explains how to package your plugin as a Ruby gem to share it with the community.","title":"Extending tomo"},{"location":"#tutorials","text":"Deploying Rails From Scratch Writing Custom Tasks Publishing a Plugin","title":"Tutorials"},{"location":"#blog-posts","text":"Automate Rails deployments with GitHub Actions","title":"Blog posts"},{"location":"#reference-documentation","text":"Configuration Commands init setup deploy run tasks Plugins core bundler env git nodenv puma rails rbenv API Host Logger Paths PluginDSL Remote Result TaskLibrary Testing::MockPluginTester","title":"Reference documentation"},{"location":"#faq","text":"","title":"FAQ"},{"location":"#what-does-the-unsupported-option-accept-new-error-mean","text":"By default, tomo uses the \u201caccept-new\u201d value for the StrictHostKeyChecking option, which is supported by OpenSSH 7.6 and newer. If you are using an older version, this will cause an error. As a workaround, you can override tomo\u2019s default behavior like this: # Replace \"accept-new\" with something compatible with older versions of SSH set ssh_strict_host_key_checking: true # or false","title":"What does the unsupported option \"accept-new\" error mean?"},{"location":"#can-i-deploy-multiple-apps-to-a-single-host","text":"Tomo relies on the host user\u2019s bash profile for various things, like setting environment variables and initializing rbenv and nodenv. This makes it impractical to deploy multiple apps to a single host using the same deploy user. The solution is to create multiple users on the remote host, and then configure a different user for deploying each app. That way each user can have its own distinct environment variables and you can easily configure each app differently without risking conflicts. Refer to the tomo Rails tutorial for instructions on creating a deploy user. E.g. app1 would be configured to deploy as: host \"app1@example.com\" And app2 would be configured to deploy as: host \"app2@example.com\" Next run tomo setup for both apps; this will set everything up for both users on the remote host (environment variables, rbenv, etc.). You can now deploy both apps to the same host, with the confidence that their configurations will be kept cleanly separated.","title":"Can I deploy multiple apps to a single host?"},{"location":"#does-tomo-support-git-submodules","text":"No, not out of the box. However, you can extend tomo with an additional task for submodules; see the solution in PR #220 suggested by @numbcoder .","title":"Does tomo support git submodules?"},{"location":"#support","text":"This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you\u2019d like to help by submitting a pull request, or if you\u2019ve discovered a bug that needs my attention, please let me know. Check out CONTRIBUTING.md to get started. Happy hacking! \u2014Matt","title":"Support"},{"location":"#license","text":"The gem is available as open source under the terms of the MIT License .","title":"License"},{"location":"#code-of-conduct","text":"Everyone interacting in the Tomo project\u2019s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct .","title":"Code of conduct"},{"location":"#contribution-guide","text":"Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short CONTRIBUTING.md guidelines before you dive in.","title":"Contribution guide"},{"location":"comparisons/","text":"Comparisons Tomo is a SSH-based deployment tool written in Ruby, and so it is natural to compare it with the many other popular Ruby tools in that category. Here are some specific design choices that make tomo different. Proven built-in Rails support. Tomo includes all the setup and deployment tasks you need to deploy a basic Rails app. On top of that, every release of tomo is automatically tested to verify that it can successfully deploy the latest versions of Rails, Bundler, and Ruby out of the box. Opinionated defaults. The tasks built-into tomo provide a very opinionated deployment: rbenv and nodenv to install ruby and node, puma via systemd with socket activation for zero-downtime restarts, 12-factor style configuration (environment variables as opposed to configuration files), and so on. Tomo provides a strong set of production-tested conventions without you needing to piece together snippets from blog posts or tutorials. Easy to extend. Unlike other Ruby deployment tools that layer DSL monkey patches on top of rake, tomo is built from the ground up with modern CLI conventions, testability, and simplicity in mind. Tomo takes care of SSH connection management for you, and the ordering of deployment and setup operations is purely configuration based. There are no complex chains of prerequisites or programmatic before/after hooks to deal with. All you have to do is write plain Ruby methods utilizing a concise API. Code example To get an idea of how much simpler tomo can be, here is an example of the same task implemented in capistrano vs tomo: # capistrano task :precompile do on release_roles(fetch(:assets_roles)) do within release_path do with rails_env: fetch(:rails_env), rails_groups: fetch(:rails_assets_groups) do execute :rake, \"assets:precompile\" end end end end # tomo def assets_precompile remote.rake(\"assets:precompile\") end Features Here\u2019s how tomo compares with the most popular Ruby-based deployment tools: Capistrano and Mina. Tomo Capistrano Mina First release 2019 2009 2012 Required gem dependencies 0 7 2 Minimum supported ruby version 3.1 2.0 2.0 Configuration files 1 (.tomo/config.rb) 3+ (Capfile, config/deploy.rb, config/deploy/*.rb per stage) 1 (config/deploy.rb) Deploy in parallel to multiple hosts \u2705 Yes \u2705 Yes \u274c No Configure multiple environments/stages \u2705 Yes \u2705 Yes \u2705 Yes, via custom rake tasks Simulate a deploy (dry run) \u2705 Yes \u2705 Yes \u2705 Yes Customizable deploy command \u2705 Yes, via config \u2705 Yes, via rake tasks that attach before/after hooks \u2705 Yes, via rake task Built-in setup or \u201ccold deploy\u201d command \u2705 Yes \u274c No \u2705 Yes Automated host setup (ruby, bundler, yarn, etc.) \u2705 Yes \u274c No \u274c No Rollback command \u274c No \u2705 Yes \u2705 Yes Manage host environment variables (SECRET_KEY_BASE, DATABASE_URL, etc.) \u2705 Yes \u274c No \u274c No Automated testing of deploying Rails end-to-end \u2705 Yes \u274c No \u274c No Built-in Rails web server tasks \u2705 Yes, puma with systemd \u274c No \u274c No Built-in ruby version manager tasks \u2705 Yes, rbenv \u274c No \u2705 Yes, chruby, rbenv, rvm, ry Built-in node version manager tasks \u2705 Yes, nodenv \u274c No \u274c No SSH command execution One at a time One at a time Batched SSH implementation Native Ruby (net-ssh) Native SSH connection management Automatic Explicit Automatic Task framework Tomo-specific Rake Rake Tasks can invoke other tasks \u274c No \u2705 Yes \u2705 Yes Tasks can modify settings on the fly \u274c No \u2705 Yes \u2705 Yes Tasks can have before/after hooks \u274c No \u2705 Yes, via DSL \u2705 Yes, via rake DSL availability Inside task definitions only Global mixin (monkey patch) Global mixin (monkey patch) DSL for running scripts locally (as opposed to on remote host) \u274c No \u2705 Yes \u2705 Yes DSL for uploading ERB templates \u2705 Yes \u274c No \u274c No","title":"Comparisons"},{"location":"comparisons/#comparisons","text":"Tomo is a SSH-based deployment tool written in Ruby, and so it is natural to compare it with the many other popular Ruby tools in that category. Here are some specific design choices that make tomo different. Proven built-in Rails support. Tomo includes all the setup and deployment tasks you need to deploy a basic Rails app. On top of that, every release of tomo is automatically tested to verify that it can successfully deploy the latest versions of Rails, Bundler, and Ruby out of the box. Opinionated defaults. The tasks built-into tomo provide a very opinionated deployment: rbenv and nodenv to install ruby and node, puma via systemd with socket activation for zero-downtime restarts, 12-factor style configuration (environment variables as opposed to configuration files), and so on. Tomo provides a strong set of production-tested conventions without you needing to piece together snippets from blog posts or tutorials. Easy to extend. Unlike other Ruby deployment tools that layer DSL monkey patches on top of rake, tomo is built from the ground up with modern CLI conventions, testability, and simplicity in mind. Tomo takes care of SSH connection management for you, and the ordering of deployment and setup operations is purely configuration based. There are no complex chains of prerequisites or programmatic before/after hooks to deal with. All you have to do is write plain Ruby methods utilizing a concise API.","title":"Comparisons"},{"location":"comparisons/#code-example","text":"To get an idea of how much simpler tomo can be, here is an example of the same task implemented in capistrano vs tomo: # capistrano task :precompile do on release_roles(fetch(:assets_roles)) do within release_path do with rails_env: fetch(:rails_env), rails_groups: fetch(:rails_assets_groups) do execute :rake, \"assets:precompile\" end end end end # tomo def assets_precompile remote.rake(\"assets:precompile\") end","title":"Code example"},{"location":"comparisons/#features","text":"Here\u2019s how tomo compares with the most popular Ruby-based deployment tools: Capistrano and Mina. Tomo Capistrano Mina First release 2019 2009 2012 Required gem dependencies 0 7 2 Minimum supported ruby version 3.1 2.0 2.0 Configuration files 1 (.tomo/config.rb) 3+ (Capfile, config/deploy.rb, config/deploy/*.rb per stage) 1 (config/deploy.rb) Deploy in parallel to multiple hosts \u2705 Yes \u2705 Yes \u274c No Configure multiple environments/stages \u2705 Yes \u2705 Yes \u2705 Yes, via custom rake tasks Simulate a deploy (dry run) \u2705 Yes \u2705 Yes \u2705 Yes Customizable deploy command \u2705 Yes, via config \u2705 Yes, via rake tasks that attach before/after hooks \u2705 Yes, via rake task Built-in setup or \u201ccold deploy\u201d command \u2705 Yes \u274c No \u2705 Yes Automated host setup (ruby, bundler, yarn, etc.) \u2705 Yes \u274c No \u274c No Rollback command \u274c No \u2705 Yes \u2705 Yes Manage host environment variables (SECRET_KEY_BASE, DATABASE_URL, etc.) \u2705 Yes \u274c No \u274c No Automated testing of deploying Rails end-to-end \u2705 Yes \u274c No \u274c No Built-in Rails web server tasks \u2705 Yes, puma with systemd \u274c No \u274c No Built-in ruby version manager tasks \u2705 Yes, rbenv \u274c No \u2705 Yes, chruby, rbenv, rvm, ry Built-in node version manager tasks \u2705 Yes, nodenv \u274c No \u274c No SSH command execution One at a time One at a time Batched SSH implementation Native Ruby (net-ssh) Native SSH connection management Automatic Explicit Automatic Task framework Tomo-specific Rake Rake Tasks can invoke other tasks \u274c No \u2705 Yes \u2705 Yes Tasks can modify settings on the fly \u274c No \u2705 Yes \u2705 Yes Tasks can have before/after hooks \u274c No \u2705 Yes, via DSL \u2705 Yes, via rake DSL availability Inside task definitions only Global mixin (monkey patch) Global mixin (monkey patch) DSL for running scripts locally (as opposed to on remote host) \u274c No \u2705 Yes \u2705 Yes DSL for uploading ERB templates \u2705 Yes \u274c No \u274c No","title":"Features"},{"location":"configuration/","text":"Configuration Tomo is configured via a .tomo/config.rb file. This configuration file defines what tasks to run when executing a setup or deploy , the settings that affect the behavior of those tasks, and the remote host or hosts where those tasks will be run. The format of tomo\u2019s configuration file is designed to be simple and concise for basic deployments, with the flexibility to scale to more advanced setups that involve multiple roles, environments, and hosts. A basic deployment will typically use these configuration directives: plugin host set setup deploy Here\u2019s an abbreviated example: plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"deployer@app.example.com\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end A more complex deployment may make use of these additional directives: environment role batch plugin(name_or_relative_path) Load a tomo plugin by name or from a Ruby file by a relative path. Several plugins are built into tomo: bundler , env , git , nodenv , puma , rails , and rbenv . If you want to use the tasks provided by one of these plugins, load it by name, like this: plugin \"git\" Plugins can also be provided by gems installed on your system. For example, the tomo-plugin-sidekiq gem provides the \u201csidekiq\u201d plugin. Make sure the gem is installed (e.g. in your Gemfile) and then reference the plugin by name to load it: plugin \"sidekiq\" Note that the name of the plugin may not necessarily match the name of the gem. Refer to the gem\u2019s documentation for installation instructions. Finally, if the argument to plugin starts with a dot ( . ) it is considered a relative path to a custom plugin. By convention, custom plugins are stored in .tomo/plugins/ within the project that tomo is deploying. The name of the plugin is inferred from its file name. So for example, if the plugin is loaded from a file named foo.rb , then the name of the plugin is \u201cfoo\u201d and all tasks it defines will be given the foo: namespace: plugin \"./plugins/foo.rb\" host(address, **options) Specify the SSH host address (including username) that tomo will connect to. For example: host \"deployer@app.example.com\" # port 22 is implied host \"admin@192.168.1.50\", port: 8022 # port 8022 The following advanced options are supported: Name Purpose Default port SSH port number. 22 roles An array of String roles to assign to this host. Used with the role directive for specifying which tasks should run on this host. [] log_prefix A String prefix to print next to all log output for this host. nil privileged_user The SSH user to connect as when running privileged tasks. See setup for an example. \"root\" set(hash) Specify a value for a tomo setting. For example, to change the number of releases that tomo retains when pruning old releases: set keep_releases: 5 For a full list of settings that affect tomo\u2019s core behavior, refer to the core plugin documentation . Each plugin such as bundler and git also has its own specialized list of settings. Refer to the each plugin\u2019s documentation for a full reference. Interpolation It is possible to reference other settings when specifying a value. The format of a reference string is %{name} where name is the name of another setting. This is often used to build paths that are relative to the release that is being deployed, or for paths relative to tomo\u2019s shared directory. In this example, the value will be interpolated to contain the release that is being deployed: set release_json_path: \"%{release_path}/.tomo_release.json\" # => \"/var/www/my-app/20190523234156/.tomo_release.json\" Another common use case is the shared directory: set bundler_path: \"%{shared_path}/bundle\" # => \"/var/www/my-app/shared/bundle\" Interpolation takes place after tomo has loaded all configuration, plugins, and overrides, just before tasks are run. Custom settings set will define a setting if it does not already exist. This means you can create arbitrarily-named settings for your own purposes, such as for use within custom tasks. set my_setting_i_just_made_up: \"great\" In practice most settings are strings, but any Ruby value is possible. set some_double: 0.57 set my_hash: { urgent: true, message: \"hello\" } Overrides Settings defined by set can be overridden when running a tomo command, e.g. tomo deploy , by way of environment variables and command-line arguments. Environment variable overrides take the form of TOMO_* . For example, this will override the :git_branch setting to be \u201cdevelop\u201d: $ export TOMO_GIT_BRANCH=develop $ tomo deploy On the command line, -s or --setting can be used. For example: $ tomo deploy -s git_branch=develop The precedence of overrides is as follows (higher in the list have higher precedence): Command-line overrides Environment variable overrides set Defaults (specified by plugins) setup(&block) Define the list of tasks that will be run by the tomo setup command, by providing a block containing run directives, like this: setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Each run can optionally take a privileged: true option. When specified, the task will be run using the \u201croot\u201d user instead of the default user specified for each host . setup do run \"apt:install\", privileged: true end deploy(&block) Define the list of tasks that will be run by the tomo deploy command, by providing a block containing run directives, like this: deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end environment(name, &block) Define an environment so that tomo can be used to deploy the same project with more than one set of configuration. Each environment must have a unique name and can contain its own host and set directives. For example: # Top-level config is shared by both environments set git_url: \"git@github.com:username/repo.git\" environment :staging do host \"deployer@staging.example.com\" set git_branch: \"develop\" end environment :production do host \"deployer@app.example.com\" set git_branch: \"main\" end Use the -e or --environment option when running tomo to select which environment to use. role(name, runs:) Specify that certain task(s) are only allowed to run on hosts that have the role name . The runs option must be an array of Strings representing task names. Simple wildcards (glob rules using * ) can be used to match multiple tasks. By default, every task that is listed in setup and deploy blocks is run on every host. In a multi-host deployment this is not always desirable. For example, the rails:db_seed and rails:db_migrate tasks should only be run once per deployment (i.e. on one host). To accomplish this, we can define a role named \u201cdb\u201d that is responsible for running these tasks, like this: role \"db\", runs: [\"rails:db_*\"] host \"deployer@app1.example.com\", roles: [\"db\"] host \"deployer@app2.example.com\", roles: [] The role directive in the example above tells tomo that any task matching the glob pattern rails:db_* should only run on hosts that are assigned the \u201cdb\u201d role. That means that app1.example.com will run rails:db_seed and rails:db_migrate , but app2.example.com will not. batch(&block) Define a group tasks to run in parallel during a multi-host deploy. This allows one host to \u201crace ahead\u201d of other hosts and leads to potentially faster deployments. In a multi-host deployment, by default each task in a setup and deploy must complete on all hosts before tomo will move onto the next task. This means a deployment is limited by its slowest host. If a task is configured via role to run on only one host (e.g. rails:db_migrate ), other hosts must wait until the task is done. We can speed this up by using batch , as in this example: deploy do # All tasks in this batch must complete before tomo will move onto # core:symlink_current, but within the batch each host can \"race ahead\" # independently in parallel. batch do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" end # This task must complete on all hosts before moving onto the next batch. run \"core:symlink_current\" # The tasks within this batch can run independently in parallel on each host. batch do run \"puma:restart\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end end At runtime, tomo turns this configuration into an \u201cexecution plan\u201d, which you can see by passing the --debug option to tomo deploy . Here\u2019s what the execution plan might look like for the above configuration with two hosts: DEBUG: Execution plan: CONCURRENTLY (2 THREADS): = CONNECT deployer@app1.example.com = CONNECT deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN env:update ON deployer@app1.example.com RUN git:create_release ON deployer@app1.example.com RUN core:symlink_shared ON deployer@app1.example.com RUN core:write_release_json ON deployer@app1.example.com RUN bundler:install ON deployer@app1.example.com RUN rails:assets_precompile ON deployer@app1.example.com RUN rails:db_migrate ON deployer@app1.example.com = IN SEQUENCE: RUN env:update ON deployer@app2.example.com RUN git:create_release ON deployer@app2.example.com RUN core:symlink_shared ON deployer@app2.example.com RUN core:write_release_json ON deployer@app2.example.com RUN bundler:install ON deployer@app2.example.com RUN rails:assets_precompile ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = RUN core:symlink_current ON deployer@app1.example.com = RUN core:symlink_current ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN puma:restart ON deployer@app1.example.com RUN core:clean_releases ON deployer@app1.example.com RUN bundler:clean ON deployer@app1.example.com RUN core:log_revision ON deployer@app1.example.com = IN SEQUENCE: RUN puma:restart ON deployer@app2.example.com RUN core:clean_releases ON deployer@app2.example.com RUN bundler:clean ON deployer@app2.example.com RUN core:log_revision ON deployer@app2.example.com As we can see, core:symlink_current is executed at the same time on both hosts, but before and after that, the other tasks can be executed out of sync.","title":"Configuration"},{"location":"configuration/#configuration","text":"Tomo is configured via a .tomo/config.rb file. This configuration file defines what tasks to run when executing a setup or deploy , the settings that affect the behavior of those tasks, and the remote host or hosts where those tasks will be run. The format of tomo\u2019s configuration file is designed to be simple and concise for basic deployments, with the flexibility to scale to more advanced setups that involve multiple roles, environments, and hosts. A basic deployment will typically use these configuration directives: plugin host set setup deploy Here\u2019s an abbreviated example: plugin \"git\" plugin \"bundler\" plugin \"rails\" # ... host \"deployer@app.example.com\" set application: \"my-rails-app\" set deploy_to: \"/var/www/%{application}\" set git_url: \"git@github.com:my-username/my-rails-app.git\" set git_branch: \"main\" # ... setup do run \"git:clone\" run \"git:create_release\" run \"bundler:install\" run \"rails:db_schema_load\" # ... end deploy do run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" run \"core:symlink_current\" # ... end A more complex deployment may make use of these additional directives: environment role batch","title":"Configuration"},{"location":"configuration/#pluginname_or_relative_path","text":"Load a tomo plugin by name or from a Ruby file by a relative path. Several plugins are built into tomo: bundler , env , git , nodenv , puma , rails , and rbenv . If you want to use the tasks provided by one of these plugins, load it by name, like this: plugin \"git\" Plugins can also be provided by gems installed on your system. For example, the tomo-plugin-sidekiq gem provides the \u201csidekiq\u201d plugin. Make sure the gem is installed (e.g. in your Gemfile) and then reference the plugin by name to load it: plugin \"sidekiq\" Note that the name of the plugin may not necessarily match the name of the gem. Refer to the gem\u2019s documentation for installation instructions. Finally, if the argument to plugin starts with a dot ( . ) it is considered a relative path to a custom plugin. By convention, custom plugins are stored in .tomo/plugins/ within the project that tomo is deploying. The name of the plugin is inferred from its file name. So for example, if the plugin is loaded from a file named foo.rb , then the name of the plugin is \u201cfoo\u201d and all tasks it defines will be given the foo: namespace: plugin \"./plugins/foo.rb\"","title":"plugin(name_or_relative_path)"},{"location":"configuration/#hostaddress-options","text":"Specify the SSH host address (including username) that tomo will connect to. For example: host \"deployer@app.example.com\" # port 22 is implied host \"admin@192.168.1.50\", port: 8022 # port 8022 The following advanced options are supported: Name Purpose Default port SSH port number. 22 roles An array of String roles to assign to this host. Used with the role directive for specifying which tasks should run on this host. [] log_prefix A String prefix to print next to all log output for this host. nil privileged_user The SSH user to connect as when running privileged tasks. See setup for an example. \"root\"","title":"host(address, **options)"},{"location":"configuration/#sethash","text":"Specify a value for a tomo setting. For example, to change the number of releases that tomo retains when pruning old releases: set keep_releases: 5 For a full list of settings that affect tomo\u2019s core behavior, refer to the core plugin documentation . Each plugin such as bundler and git also has its own specialized list of settings. Refer to the each plugin\u2019s documentation for a full reference.","title":"set(hash)"},{"location":"configuration/#interpolation","text":"It is possible to reference other settings when specifying a value. The format of a reference string is %{name} where name is the name of another setting. This is often used to build paths that are relative to the release that is being deployed, or for paths relative to tomo\u2019s shared directory. In this example, the value will be interpolated to contain the release that is being deployed: set release_json_path: \"%{release_path}/.tomo_release.json\" # => \"/var/www/my-app/20190523234156/.tomo_release.json\" Another common use case is the shared directory: set bundler_path: \"%{shared_path}/bundle\" # => \"/var/www/my-app/shared/bundle\" Interpolation takes place after tomo has loaded all configuration, plugins, and overrides, just before tasks are run.","title":"Interpolation"},{"location":"configuration/#custom-settings","text":"set will define a setting if it does not already exist. This means you can create arbitrarily-named settings for your own purposes, such as for use within custom tasks. set my_setting_i_just_made_up: \"great\" In practice most settings are strings, but any Ruby value is possible. set some_double: 0.57 set my_hash: { urgent: true, message: \"hello\" }","title":"Custom settings"},{"location":"configuration/#overrides","text":"Settings defined by set can be overridden when running a tomo command, e.g. tomo deploy , by way of environment variables and command-line arguments. Environment variable overrides take the form of TOMO_* . For example, this will override the :git_branch setting to be \u201cdevelop\u201d: $ export TOMO_GIT_BRANCH=develop $ tomo deploy On the command line, -s or --setting can be used. For example: $ tomo deploy -s git_branch=develop The precedence of overrides is as follows (higher in the list have higher precedence): Command-line overrides Environment variable overrides set Defaults (specified by plugins)","title":"Overrides"},{"location":"configuration/#setupblock","text":"Define the list of tasks that will be run by the tomo setup command, by providing a block containing run directives, like this: setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Each run can optionally take a privileged: true option. When specified, the task will be run using the \u201croot\u201d user instead of the default user specified for each host . setup do run \"apt:install\", privileged: true end","title":"setup(&block)"},{"location":"configuration/#deployblock","text":"Define the list of tasks that will be run by the tomo deploy command, by providing a block containing run directives, like this: deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end","title":"deploy(&block)"},{"location":"configuration/#environmentname-block","text":"Define an environment so that tomo can be used to deploy the same project with more than one set of configuration. Each environment must have a unique name and can contain its own host and set directives. For example: # Top-level config is shared by both environments set git_url: \"git@github.com:username/repo.git\" environment :staging do host \"deployer@staging.example.com\" set git_branch: \"develop\" end environment :production do host \"deployer@app.example.com\" set git_branch: \"main\" end Use the -e or --environment option when running tomo to select which environment to use.","title":"environment(name, &block)"},{"location":"configuration/#rolename-runs","text":"Specify that certain task(s) are only allowed to run on hosts that have the role name . The runs option must be an array of Strings representing task names. Simple wildcards (glob rules using * ) can be used to match multiple tasks. By default, every task that is listed in setup and deploy blocks is run on every host. In a multi-host deployment this is not always desirable. For example, the rails:db_seed and rails:db_migrate tasks should only be run once per deployment (i.e. on one host). To accomplish this, we can define a role named \u201cdb\u201d that is responsible for running these tasks, like this: role \"db\", runs: [\"rails:db_*\"] host \"deployer@app1.example.com\", roles: [\"db\"] host \"deployer@app2.example.com\", roles: [] The role directive in the example above tells tomo that any task matching the glob pattern rails:db_* should only run on hosts that are assigned the \u201cdb\u201d role. That means that app1.example.com will run rails:db_seed and rails:db_migrate , but app2.example.com will not.","title":"role(name, runs:)"},{"location":"configuration/#batchblock","text":"Define a group tasks to run in parallel during a multi-host deploy. This allows one host to \u201crace ahead\u201d of other hosts and leads to potentially faster deployments. In a multi-host deployment, by default each task in a setup and deploy must complete on all hosts before tomo will move onto the next task. This means a deployment is limited by its slowest host. If a task is configured via role to run on only one host (e.g. rails:db_migrate ), other hosts must wait until the task is done. We can speed this up by using batch , as in this example: deploy do # All tasks in this batch must complete before tomo will move onto # core:symlink_current, but within the batch each host can \"race ahead\" # independently in parallel. batch do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:assets_precompile\" run \"rails:db_migrate\" end # This task must complete on all hosts before moving onto the next batch. run \"core:symlink_current\" # The tasks within this batch can run independently in parallel on each host. batch do run \"puma:restart\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end end At runtime, tomo turns this configuration into an \u201cexecution plan\u201d, which you can see by passing the --debug option to tomo deploy . Here\u2019s what the execution plan might look like for the above configuration with two hosts: DEBUG: Execution plan: CONCURRENTLY (2 THREADS): = CONNECT deployer@app1.example.com = CONNECT deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN env:update ON deployer@app1.example.com RUN git:create_release ON deployer@app1.example.com RUN core:symlink_shared ON deployer@app1.example.com RUN core:write_release_json ON deployer@app1.example.com RUN bundler:install ON deployer@app1.example.com RUN rails:assets_precompile ON deployer@app1.example.com RUN rails:db_migrate ON deployer@app1.example.com = IN SEQUENCE: RUN env:update ON deployer@app2.example.com RUN git:create_release ON deployer@app2.example.com RUN core:symlink_shared ON deployer@app2.example.com RUN core:write_release_json ON deployer@app2.example.com RUN bundler:install ON deployer@app2.example.com RUN rails:assets_precompile ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = RUN core:symlink_current ON deployer@app1.example.com = RUN core:symlink_current ON deployer@app2.example.com CONCURRENTLY (2 THREADS): = IN SEQUENCE: RUN puma:restart ON deployer@app1.example.com RUN core:clean_releases ON deployer@app1.example.com RUN bundler:clean ON deployer@app1.example.com RUN core:log_revision ON deployer@app1.example.com = IN SEQUENCE: RUN puma:restart ON deployer@app2.example.com RUN core:clean_releases ON deployer@app2.example.com RUN bundler:clean ON deployer@app2.example.com RUN core:log_revision ON deployer@app2.example.com As we can see, core:symlink_current is executed at the same time on both hosts, but before and after that, the other tasks can be executed out of sync.","title":"batch(&block)"},{"location":"api/Host/","text":"Tomo::Host Represents a remote SSH host. host.address # => \"example.com\" host.port # => 22 host.user # => \"deployer\" host.roles # => [\"app\", \"db\"] host.to_s # => \"deployer@example.com\" A Host is always frozen and cannot be modified. Instance methods address \u2192 String The host name or IP address. port \u2192 Integer The SSH port, usually 22. user \u2192 String The username used when connecting to the host via SSH. roles \u2192 [String] An array of roles that are assigned to this host. Roles are used in multi-host deployments to control which tasks are run on which hosts. to_s \u2192 String A representation of host in the form of user@address:port . If the port is 22, that portion is omitted.","title":"Tomo::Host"},{"location":"api/Host/#tomohost","text":"Represents a remote SSH host. host.address # => \"example.com\" host.port # => 22 host.user # => \"deployer\" host.roles # => [\"app\", \"db\"] host.to_s # => \"deployer@example.com\" A Host is always frozen and cannot be modified.","title":"Tomo::Host"},{"location":"api/Host/#instance-methods","text":"","title":"Instance methods"},{"location":"api/Host/#address-string","text":"The host name or IP address.","title":"address \u2192 String"},{"location":"api/Host/#port-integer","text":"The SSH port, usually 22.","title":"port \u2192 Integer"},{"location":"api/Host/#user-string","text":"The username used when connecting to the host via SSH.","title":"user \u2192 String"},{"location":"api/Host/#roles-string","text":"An array of roles that are assigned to this host. Roles are used in multi-host deployments to control which tasks are run on which hosts.","title":"roles \u2192 [String]"},{"location":"api/Host/#to_s-string","text":"A representation of host in the form of user@address:port . If the port is 22, that portion is omitted.","title":"to_s \u2192 String"},{"location":"api/Logger/","text":"Tomo::Logger Provides a simple interface for logging messages to stdout and stderr. In multi-host deployments, messages are automatically prefixed with [1] , [2] , etc. based on current host. This makes it easier to distinguish where log messages are coming from. $ tomo run bundler:clean tomo run v1.0.0 [1] \u2192 Connecting to deployer@web1.example.com [2] \u2192 Connecting to deployer@web2.example.com [1] \u2022 bundler:clean [2] \u2022 bundler:clean [1] cd /home/deployer/apps/my-app/current && bundle clean [2] cd /home/deployer/apps/my-app/current && bundle clean \u2714 Ran bundler:clean on deployer@web1.example.com and deployer@web2.example.com If tomo is run in --dry-run mode, log messages are prefixed with a * to indicate the commands are being simulated. $ tomo run bundler:clean --dry-run tomo run v1.0.0 * [1] \u2192 Connecting to deployer@web1.example.com * [2] \u2192 Connecting to deployer@web2.example.com * [1] \u2022 bundler:clean * [2] \u2022 bundler:clean * [1] cd /home/deployer/apps/my-app/current && bundle clean * [2] cd /home/deployer/apps/my-app/current && bundle clean * Simulated bundler:clean on deployer@web1.example.com and deployer@web2.example.com (dry run) Instance methods debug(message) \u2192 nil Prints a message to stderr in gray with a DEBUG: prefix. Debug messages are only shown if tomo is run with the --debug option. Otherwise this is a no-op. info(message) \u2192 nil Prints a message to stdout . warn(message) \u2192 nil Prints a message to stderr with a red WARNING: prefix. error(message) \u2192 nil Prints a message to stderr with a red ERROR: prefix, indented, and with leading and trailing blank lines for extra emphasis.","title":"Tomo::Logger"},{"location":"api/Logger/#tomologger","text":"Provides a simple interface for logging messages to stdout and stderr. In multi-host deployments, messages are automatically prefixed with [1] , [2] , etc. based on current host. This makes it easier to distinguish where log messages are coming from. $ tomo run bundler:clean tomo run v1.0.0 [1] \u2192 Connecting to deployer@web1.example.com [2] \u2192 Connecting to deployer@web2.example.com [1] \u2022 bundler:clean [2] \u2022 bundler:clean [1] cd /home/deployer/apps/my-app/current && bundle clean [2] cd /home/deployer/apps/my-app/current && bundle clean \u2714 Ran bundler:clean on deployer@web1.example.com and deployer@web2.example.com If tomo is run in --dry-run mode, log messages are prefixed with a * to indicate the commands are being simulated. $ tomo run bundler:clean --dry-run tomo run v1.0.0 * [1] \u2192 Connecting to deployer@web1.example.com * [2] \u2192 Connecting to deployer@web2.example.com * [1] \u2022 bundler:clean * [2] \u2022 bundler:clean * [1] cd /home/deployer/apps/my-app/current && bundle clean * [2] cd /home/deployer/apps/my-app/current && bundle clean * Simulated bundler:clean on deployer@web1.example.com and deployer@web2.example.com (dry run)","title":"Tomo::Logger"},{"location":"api/Logger/#instance-methods","text":"","title":"Instance methods"},{"location":"api/Logger/#debugmessage-nil","text":"Prints a message to stderr in gray with a DEBUG: prefix. Debug messages are only shown if tomo is run with the --debug option. Otherwise this is a no-op.","title":"debug(message) \u2192 nil"},{"location":"api/Logger/#infomessage-nil","text":"Prints a message to stdout .","title":"info(message) \u2192 nil"},{"location":"api/Logger/#warnmessage-nil","text":"Prints a message to stderr with a red WARNING: prefix.","title":"warn(message) \u2192 nil"},{"location":"api/Logger/#errormessage-nil","text":"Prints a message to stderr with a red ERROR: prefix, indented, and with leading and trailing blank lines for extra emphasis.","title":"error(message) \u2192 nil"},{"location":"api/Paths/","text":"Tomo::Paths Provides syntactic sugar for accessing settings that represent file system paths. For every tomo setting in the form :_path , Paths will expose a method of that name that behaves like a Ruby Pathname object. As a special exception, the :deploy_to setting is also exposed even though it does not follow the same naming convention. In tomo the following path settings are always available: settings[:deploy_to] # => \"/var/www/my-app\" settings[:current_path] # => \"/var/www/my-app/current\" settings[:release_path] # => \"/var/www/my-app/releases/20190531164322\" settings[:releases_path] # => \"/var/www/my-app/releases\" settings[:shared_path] # => \"/var/www/my-app/shared\" Using Paths, these same settings can be accessed like this: paths.deploy_to # => \"/var/www/my-app\" paths.current # => \"/var/www/my-app/current\" paths.release # => \"/var/www/my-app/releases/20190531164322\" paths.releases # => \"/var/www/my-app/releases\" paths.shared # => \"/var/www/my-app/shared\" More powerfully, the values returned by Paths respond to join and dirname , so you can easily compose them: paths.current.dirname # => \"/var/www/my-app\" paths.release.join(\"tmp\") # => \"/var/www/my-app/releases/20190531164322/tmp\" paths.shared.join(\"bundle\") # => \"/var/www/my-app/shared/bundle\" Paths can be used wherever a path string is expected, like chdir : remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"puma\", \"--daemon\") end # $ cd /var/www/my-app/current && bundle exec puma --daemon If a plugin defines a setting with the suffix _path or if you create your own setting with that suffix, it automatically will be exposed via the Paths object: # .tomo/config.rb set my_custom_path: \"/opt/custom\" paths.my_custom.join(\"var\") # => \"/opt/custom/var\"","title":"Tomo::Paths"},{"location":"api/Paths/#tomopaths","text":"Provides syntactic sugar for accessing settings that represent file system paths. For every tomo setting in the form :_path , Paths will expose a method of that name that behaves like a Ruby Pathname object. As a special exception, the :deploy_to setting is also exposed even though it does not follow the same naming convention. In tomo the following path settings are always available: settings[:deploy_to] # => \"/var/www/my-app\" settings[:current_path] # => \"/var/www/my-app/current\" settings[:release_path] # => \"/var/www/my-app/releases/20190531164322\" settings[:releases_path] # => \"/var/www/my-app/releases\" settings[:shared_path] # => \"/var/www/my-app/shared\" Using Paths, these same settings can be accessed like this: paths.deploy_to # => \"/var/www/my-app\" paths.current # => \"/var/www/my-app/current\" paths.release # => \"/var/www/my-app/releases/20190531164322\" paths.releases # => \"/var/www/my-app/releases\" paths.shared # => \"/var/www/my-app/shared\" More powerfully, the values returned by Paths respond to join and dirname , so you can easily compose them: paths.current.dirname # => \"/var/www/my-app\" paths.release.join(\"tmp\") # => \"/var/www/my-app/releases/20190531164322/tmp\" paths.shared.join(\"bundle\") # => \"/var/www/my-app/shared/bundle\" Paths can be used wherever a path string is expected, like chdir : remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"puma\", \"--daemon\") end # $ cd /var/www/my-app/current && bundle exec puma --daemon If a plugin defines a setting with the suffix _path or if you create your own setting with that suffix, it automatically will be exposed via the Paths object: # .tomo/config.rb set my_custom_path: \"/opt/custom\" paths.my_custom.join(\"var\") # => \"/opt/custom/var\"","title":"Tomo::Paths"},{"location":"api/PluginDSL/","text":"Tomo::PluginDSL A tomo plugin is defined by a Ruby module that extends Tomo::PluginDSL. A plugin definition can specify three things: Default settings Tasks Helpers Here is the bundler plugin as an example: require_relative \"bundler/helpers\" require_relative \"bundler/tasks\" module Tomo::Plugin::Bundler extend Tomo::PluginDSL tasks Tomo::Plugin::Bundler::Tasks helpers Tomo::Plugin::Bundler::Helpers defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end The above plugin defines several default settings, defines tasks using a TaskLibrary named Tomo::Plugin::Bundler::Tasks , and defines helpers in a module named Tomo::Plugin::Bundler::Helpers . Refer to the Publishing a Plugin tutorial for more information about packaging and distributing tomo plugins. Instance methods defaults(hash) Specify default settings that will be applied when this plugin is loaded. Although not strictly necessary, it is best practice to list all required and optional settings that are used by the plugin, even if the default values are nil . This lets other developers know what setting names are expected when using the plugin. Settings must use symbol keys and typically String values, although any Ruby type is possible. Strings can contain interpolated values . module Tomo::Plugin::Bundler extend Tomo::PluginDSL defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end tasks(*task_library_class) Specify the tasks that will be defined by this plugin by supplying one or more TaskLibrary classes. The public instance methods of each class will be turned into tomo tasks. class Tomo::Plugin::Git::Tasks < Tomo::TaskLibrary def clone # ... end def create_release # ... end end class Tomo::Plugin::Git extend Tomo::PluginDSL tasks Tomo::Plugin::Git::Tasks end You can use tasks self to define a plugin and its tasks together as a single class: class Tomo::Plugin::Git < Tomo::TaskLibrary extend Tomo::PluginDSL tasks self def clone # ... end def create_release # ... end end helpers(*module) Specify the helpers that will be defined by this plugin by supplying one or more plain Ruby modules. The modules will be mixed in at runtime to extend the Remote interface with additional methods. module Tomo::Plugin::Core::Helpers def ln_sf(target, link, **run_opts) # ... end def mkdir_p(*directories, **run_opts) # ... end end module Tomo::Plugin::Core extend Tomo::PluginDSL helpers Tomo::Plugin::Core::Helpers end","title":"Tomo::PluginDSL"},{"location":"api/PluginDSL/#tomoplugindsl","text":"A tomo plugin is defined by a Ruby module that extends Tomo::PluginDSL. A plugin definition can specify three things: Default settings Tasks Helpers Here is the bundler plugin as an example: require_relative \"bundler/helpers\" require_relative \"bundler/tasks\" module Tomo::Plugin::Bundler extend Tomo::PluginDSL tasks Tomo::Plugin::Bundler::Tasks helpers Tomo::Plugin::Bundler::Helpers defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end The above plugin defines several default settings, defines tasks using a TaskLibrary named Tomo::Plugin::Bundler::Tasks , and defines helpers in a module named Tomo::Plugin::Bundler::Helpers . Refer to the Publishing a Plugin tutorial for more information about packaging and distributing tomo plugins.","title":"Tomo::PluginDSL"},{"location":"api/PluginDSL/#instance-methods","text":"","title":"Instance methods"},{"location":"api/PluginDSL/#defaultshash","text":"Specify default settings that will be applied when this plugin is loaded. Although not strictly necessary, it is best practice to list all required and optional settings that are used by the plugin, even if the default values are nil . This lets other developers know what setting names are expected when using the plugin. Settings must use symbol keys and typically String values, although any Ruby type is possible. Strings can contain interpolated values . module Tomo::Plugin::Bundler extend Tomo::PluginDSL defaults bundler_config_path: \".bundle/config\", bundler_deployment: true, bundler_gemfile: nil, bundler_jobs: \"4\", bundler_path: \"%{shared_path}/bundle\", bundler_retry: \"3\", bundler_version: nil, bundler_without: %w[development test] end","title":"defaults(hash)"},{"location":"api/PluginDSL/#taskstask_library_class","text":"Specify the tasks that will be defined by this plugin by supplying one or more TaskLibrary classes. The public instance methods of each class will be turned into tomo tasks. class Tomo::Plugin::Git::Tasks < Tomo::TaskLibrary def clone # ... end def create_release # ... end end class Tomo::Plugin::Git extend Tomo::PluginDSL tasks Tomo::Plugin::Git::Tasks end You can use tasks self to define a plugin and its tasks together as a single class: class Tomo::Plugin::Git < Tomo::TaskLibrary extend Tomo::PluginDSL tasks self def clone # ... end def create_release # ... end end","title":"tasks(*task_library_class)"},{"location":"api/PluginDSL/#helpersmodule","text":"Specify the helpers that will be defined by this plugin by supplying one or more plain Ruby modules. The modules will be mixed in at runtime to extend the Remote interface with additional methods. module Tomo::Plugin::Core::Helpers def ln_sf(target, link, **run_opts) # ... end def mkdir_p(*directories, **run_opts) # ... end end module Tomo::Plugin::Core extend Tomo::PluginDSL helpers Tomo::Plugin::Core::Helpers end","title":"helpers(*module)"},{"location":"api/Remote/","text":"Tomo::Remote A Remote represents an SSH connection to a remote host and provides a fa\u00e7ade 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) \u2192 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] # => \" is safe & easy\" remote.run \"echo\", settings[:greeting] # $ echo \\\\ 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\u2019s default login directory (almost always this is the user\u2019s 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 \u201cattach\u201d 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) \u2192 obj Changes into the specified dir when executing a script via run or attach . Must be used with a block. This causes cd && 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) \u2192 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) \u2192 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) \u2192 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 \u2192 Tomo::Host The remote SSH host that scripts will be run on. release \u2192 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","title":"Tomo::Remote"},{"location":"api/Remote/#tomoremote","text":"A Remote represents an SSH connection to a remote host and provides a fa\u00e7ade 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","title":"Tomo::Remote"},{"location":"api/Remote/#instance-methods","text":"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.","title":"Instance methods"},{"location":"api/Remote/#runcommand-options-tomoresult","text":"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] # => \" is safe & easy\" remote.run \"echo\", settings[:greeting] # $ echo \\\\ 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\u2019s default login directory (almost always this is the user\u2019s 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","title":"run(*command, **options) \u2192 Tomo::Result"},{"location":"api/Remote/#attachcommand-options","text":"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 \u201cattach\u201d 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).","title":"attach(*command, **options)"},{"location":"api/Remote/#chdirdir-block-obj","text":"Changes into the specified dir when executing a script via run or attach . Must be used with a block. This causes cd && 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.","title":"chdir(dir, &block) \u2192 obj"},{"location":"api/Remote/#envhash-block-obj","text":"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.","title":"env(hash, &block) \u2192 obj"},{"location":"api/Remote/#prependcommand-block-obj","text":"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.","title":"prepend(*command, &block) \u2192 obj"},{"location":"api/Remote/#umaskmask-block-obj","text":"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.","title":"umask(mask, &block) \u2192 obj"},{"location":"api/Remote/#host-tomohost","text":"The remote SSH host that scripts will be run on.","title":"host \u2192 Tomo::Host"},{"location":"api/Remote/#release-hash","text":"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","title":"release \u2192 Hash"},{"location":"api/Result/","text":"Tomo::Result Represents the result of a remote SSH script. result = remote.run(\"echo\", \"hello world\") result.success? # => true result.failure? # => false result.exit_status # => 0 result.stdout # => \"hello world\\n\" result.stderr # => \"\" result.output # => \"hello world\\n\" A Result is always frozen and cannot be modified. Instance methods success? \u2192 true or false Whether the remote SSH script executed successfully. An exit status of 0 is considered success. failure? \u2192 true or false Whether the remote SSH script failed to execute. An non-zero exit status is considered a failure. exit_status \u2192 Integer The exit status returned by the remote SSH script. A status of 0 is considered success. stdout \u2192 String All data that was written to stdout by the remote SSH script. Empty string if nothing was written. stderr \u2192 String All data that was written to stderr by the remote SSH script. Empty string if nothing was written. output \u2192 String All data that was written by the remote SSH script: stdout and stderr combined, in that order. Empty string if nothing was written.","title":"Tomo::Result"},{"location":"api/Result/#tomoresult","text":"Represents the result of a remote SSH script. result = remote.run(\"echo\", \"hello world\") result.success? # => true result.failure? # => false result.exit_status # => 0 result.stdout # => \"hello world\\n\" result.stderr # => \"\" result.output # => \"hello world\\n\" A Result is always frozen and cannot be modified.","title":"Tomo::Result"},{"location":"api/Result/#instance-methods","text":"","title":"Instance methods"},{"location":"api/Result/#success-true-or-false","text":"Whether the remote SSH script executed successfully. An exit status of 0 is considered success.","title":"success? \u2192 true or false"},{"location":"api/Result/#failure-true-or-false","text":"Whether the remote SSH script failed to execute. An non-zero exit status is considered a failure.","title":"failure? \u2192 true or false"},{"location":"api/Result/#exit_status-integer","text":"The exit status returned by the remote SSH script. A status of 0 is considered success.","title":"exit_status \u2192 Integer"},{"location":"api/Result/#stdout-string","text":"All data that was written to stdout by the remote SSH script. Empty string if nothing was written.","title":"stdout \u2192 String"},{"location":"api/Result/#stderr-string","text":"All data that was written to stderr by the remote SSH script. Empty string if nothing was written.","title":"stderr \u2192 String"},{"location":"api/Result/#output-string","text":"All data that was written by the remote SSH script: stdout and stderr combined, in that order. Empty string if nothing was written.","title":"output \u2192 String"},{"location":"api/TaskLibrary/","text":"Tomo::TaskLibrary This is the primary public API for extending tomo. A TaskLibrary defines tasks. Every public instance method of a TaskLibrary becomes accessible to tomo as a task of the same name, prefixed by the name of its plugin. For example, this is how the git:clone task is defined: module Tomo::Plugin::Git class Tasks < Tomo::TaskLibrary # This becomes the implementation of the git:clone task def clone require_setting :git_url # ... end end end The TaskLibrary base class provides several useful private methods (detailed below) that allow task authors to run commands on the remote host, access tomo settings, and more. For more information on writing tasks, refer to the Writing Custom Tasks tutorial. Instance methods paths \u2192 Tomo::Paths Returns a Paths object that provides convenient access to settings representing file system paths. paths.current.join(\"lib\") # => \"/var/www/my-app/current/lib\" # ...which is syntactic sugar for: Pathname.new(settings[:current_path]).join(\"lib\") settings \u2192 Hash Returns a frozen (i.e. read-only) Hash containing all of tomo\u2019s settings. Any string interpolations will have already been applied. The keys representing the setting names are always symbols. settings[:application] # => \"my-app\" settings[:deploy_to] # => \"/var/www/my-app\" settings[:non_existing] # => nil settings.fetch(:non_existing) # => KeyError settings[:foo] = \"bar\" # => FrozenError settings.key?(:application) # => true settings.key?(:non_existing) # => false remote \u2192 Tomo::Remote Returns the Remote fa\u00e7ade that allows scripts to be run on the remote host. remote.run(\"echo\", \"hello world\") require_setting(name) \u2192 nil Raises an exception if a setting with the given name is not present. In other words, it will raise if settings[name] is nil . This can be used as a guard clause to ensure that users provide all necessary settings before a task can be run. def clone require_setting :git_url remote.run \"git\", \"clone\", settings[:git_url] end require_settings(*names) \u2192 nil Like require_setting , except it accepts an arbitrary number of setting names. Raises if any of the settings are nil . require_settings :puma_control_token, :puma_control_url merge_template(path) \u2192 String Given a local path to an ERB template, merge that template and return the resulting string. The ERB template can access the same API that tasks and helpers can access, namely: settings , paths , remote , and raw . Here is an example of an ERB template: Hello, <%= settings[:application] %>! If path begins with a \".\" it is interpreted as a path relative to the tomo configuration file. This allows for easy reference to project-specific templates. For example, given this directory structure: .tomo \u251c\u2500\u2500 config.rb \u2514\u2500\u2500 templates \u2514\u2500\u2500 unicorn.service.erb Then you could reference the template in a setting like this: # .tomo/config.rb set unicorn_service_template_path: \"./templates/unicorn.service.erb\" And merge it in a task: merge_template(paths.unicorn_service_template) dry_run? \u2192 true or false Returns true if tomo was started with the --dry-run option. This is useful if there are certain code paths you want to ensure are taken during a dry run. def install return if remote.bundle?(\"check\", *check_options) && !dry_run? remote.bundle(\"install\", *install_options) end logger \u2192 Tomo::Logger Returns the global Logger object that can be used to write messages to tomo\u2019s output. logger.debug \"got here\" logger.info \"hi!\" logger.warn \"uh oh\" die(reason) Immediately halt task execution by raising an exception. This will automatically print information to stderr about what task failed, on which host, and the reason for the failure. raw(string) \u2192 String Mark a string as a \u201craw\u201d value so that it is not automatically escaped. By default tomo applies shell escaping rules for safety. If you explicitly want to invoke shell behavior, use raw to prevent these escaping rules. remote.run \"ls\", \"$HOME/.bashrc\" # $ ls $\\HOME/.bashrc # \"$HOME/.bashrc\": No such file or directory (os error 2) remote.run \"ls\", raw(\"$HOME/.bashrc\") # $ ls $HOME/.bashrc # /home/deployer/.bashrc","title":"Tomo::TaskLibrary"},{"location":"api/TaskLibrary/#tomotasklibrary","text":"This is the primary public API for extending tomo. A TaskLibrary defines tasks. Every public instance method of a TaskLibrary becomes accessible to tomo as a task of the same name, prefixed by the name of its plugin. For example, this is how the git:clone task is defined: module Tomo::Plugin::Git class Tasks < Tomo::TaskLibrary # This becomes the implementation of the git:clone task def clone require_setting :git_url # ... end end end The TaskLibrary base class provides several useful private methods (detailed below) that allow task authors to run commands on the remote host, access tomo settings, and more. For more information on writing tasks, refer to the Writing Custom Tasks tutorial.","title":"Tomo::TaskLibrary"},{"location":"api/TaskLibrary/#instance-methods","text":"","title":"Instance methods"},{"location":"api/TaskLibrary/#paths-tomopaths","text":"Returns a Paths object that provides convenient access to settings representing file system paths. paths.current.join(\"lib\") # => \"/var/www/my-app/current/lib\" # ...which is syntactic sugar for: Pathname.new(settings[:current_path]).join(\"lib\")","title":"paths \u2192 Tomo::Paths"},{"location":"api/TaskLibrary/#settings-hash","text":"Returns a frozen (i.e. read-only) Hash containing all of tomo\u2019s settings. Any string interpolations will have already been applied. The keys representing the setting names are always symbols. settings[:application] # => \"my-app\" settings[:deploy_to] # => \"/var/www/my-app\" settings[:non_existing] # => nil settings.fetch(:non_existing) # => KeyError settings[:foo] = \"bar\" # => FrozenError settings.key?(:application) # => true settings.key?(:non_existing) # => false","title":"settings \u2192 Hash"},{"location":"api/TaskLibrary/#remote-tomoremote","text":"Returns the Remote fa\u00e7ade that allows scripts to be run on the remote host. remote.run(\"echo\", \"hello world\")","title":"remote \u2192 Tomo::Remote"},{"location":"api/TaskLibrary/#require_settingname-nil","text":"Raises an exception if a setting with the given name is not present. In other words, it will raise if settings[name] is nil . This can be used as a guard clause to ensure that users provide all necessary settings before a task can be run. def clone require_setting :git_url remote.run \"git\", \"clone\", settings[:git_url] end","title":"require_setting(name) \u2192 nil"},{"location":"api/TaskLibrary/#require_settingsnames-nil","text":"Like require_setting , except it accepts an arbitrary number of setting names. Raises if any of the settings are nil . require_settings :puma_control_token, :puma_control_url","title":"require_settings(*names) \u2192 nil"},{"location":"api/TaskLibrary/#merge_templatepath-string","text":"Given a local path to an ERB template, merge that template and return the resulting string. The ERB template can access the same API that tasks and helpers can access, namely: settings , paths , remote , and raw . Here is an example of an ERB template: Hello, <%= settings[:application] %>! If path begins with a \".\" it is interpreted as a path relative to the tomo configuration file. This allows for easy reference to project-specific templates. For example, given this directory structure: .tomo \u251c\u2500\u2500 config.rb \u2514\u2500\u2500 templates \u2514\u2500\u2500 unicorn.service.erb Then you could reference the template in a setting like this: # .tomo/config.rb set unicorn_service_template_path: \"./templates/unicorn.service.erb\" And merge it in a task: merge_template(paths.unicorn_service_template)","title":"merge_template(path) \u2192 String"},{"location":"api/TaskLibrary/#dry_run-true-or-false","text":"Returns true if tomo was started with the --dry-run option. This is useful if there are certain code paths you want to ensure are taken during a dry run. def install return if remote.bundle?(\"check\", *check_options) && !dry_run? remote.bundle(\"install\", *install_options) end","title":"dry_run? \u2192 true or false"},{"location":"api/TaskLibrary/#logger-tomologger","text":"Returns the global Logger object that can be used to write messages to tomo\u2019s output. logger.debug \"got here\" logger.info \"hi!\" logger.warn \"uh oh\"","title":"logger \u2192 Tomo::Logger"},{"location":"api/TaskLibrary/#diereason","text":"Immediately halt task execution by raising an exception. This will automatically print information to stderr about what task failed, on which host, and the reason for the failure.","title":"die(reason)"},{"location":"api/TaskLibrary/#rawstring-string","text":"Mark a string as a \u201craw\u201d value so that it is not automatically escaped. By default tomo applies shell escaping rules for safety. If you explicitly want to invoke shell behavior, use raw to prevent these escaping rules. remote.run \"ls\", \"$HOME/.bashrc\" # $ ls $\\HOME/.bashrc # \"$HOME/.bashrc\": No such file or directory (os error 2) remote.run \"ls\", raw(\"$HOME/.bashrc\") # $ ls $HOME/.bashrc # /home/deployer/.bashrc","title":"raw(string) \u2192 String"},{"location":"api/testing/MockPluginTester/","text":"Tomo::Testing::MockPluginTester MockPluginTester is a helper object that allows tasks and helpers provided by plugins to be easily unit tested. It has no test framework dependencies so it can be used in Minitest, RSpec, or the testing framework of your choice. MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. For example: require \"tomo/testing\" def test_setup_directories tester = Tomo::Testing::MockPluginTester.new(settings: { deploy_to: \"/app\" }) tester.run_task(\"core:setup_directories\") assert_equal(\"mkdir -p /app /app/releases /app/shared\", tester.executed_script) end You can change the default mocking behavior by using mock_script_result , like this: require \"tomo/testing\" def test_install tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts ) end Every MockPluginTester instance loads a fresh, independent tomo environment, so mocks, plugins, settings, etc. specified in one tester will not affect any other tests. Note that you must require \"tomo/testing\" to use MockPluginTester. Class methods new(*plugin_names, settings: {}, release: {}) \u2192 new_tester Build a new MockPluginTester that loads the given list of plugin_names . The resulting tester object can be used to simulate any tasks or helpers that are provided by these plugins. Note that the \u201ccore\u201d plugin is always loaded implicitly and does not need to be specified. Any settings that are specified will be applied after the defaults settings provided by the plugins have been defined. These settings can use template strings just like set . require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"puma\", settings: { application: \"test\", current_path: \"/app/current\" } ) Any release data specified will be available to the task under test via remote.release . Instance methods run_task(task, *args) \u2192 nil Run the given task by its fully qualified name (the namespace is required). Any args , if specified, are passed to the task via settings[:run_args] . Any remote SSH scripts run by the task (e.g. via remote.run ) will be mocked according to rules previously supplied to mock_script_result . If a mock result has not been explicitly supplied, the script will use a default mock that returns a successful result with no output. require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts ) call_helper(helper, *args, **kwargs) \u2192 obj Invoke the specified helper method name with the optional positional args and keyword kwargs . Returns the return value of the helper. Remote SSH scripts are mocked as explained in run_task . require \"tomo/testing\" def test_capture_returns_stdout_not_stderr tester = Tomo::Testing::MockPluginTester.new tester.mock_script_result(stderr: \"oh no\", stdout: \"hello world\\n\") captured = tester.call_helper(:capture, \"greet\") assert_equal(\"hello world\\n\", captured) end mock_script_result(script=/.*/, stdout: \u201c\u201d, stderr: \u201c\u201d, exit_status: 0) \u2192 self Mock the return value of remote SSH scripts that match the given script . If script is a String, the mock rule will apply only to scripts that match this String exactly. If script is Regexp, the mock rule will apply to any scripts that match that pattern. If script is omitted, the mock rule will apply always. In this example, any task or helper invoked via this tester that runs readline /app/current will receive the given mock stdout response: tester.mock_script_result(\"readlink /app/current\", stdout: <<~OUT) /app/releases/20190420203028 OUT Here, any script that includes systemctl will fail with an exit status of 1: tester.mock_script_result(/systemctl/, exit_status: 1) This mocks all scripts to fail with exit status of 255 and stderr of \u201coh no!\u201d: tester.mock_script_result(exit_status: 255, stderr: \"oh no!\") executed_script \u2192 String The remote SSH script that was run by a previous invocation of run_task or call_helper . If no script was run, then nil is returned. If more that one script was run, this will raise a RuntimeError . executed_scripts \u2192 [String] All remote SSH scripts that were run by a previous invocation of run_task or call_helper . If no script was run, then an empty array is returned. stdout \u2192 String Everything that was written to stdout during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned. stderr \u2192 String Everything that was written to stderr during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned.","title":"Tomo::Testing::MockPluginTester"},{"location":"api/testing/MockPluginTester/#tomotestingmockplugintester","text":"MockPluginTester is a helper object that allows tasks and helpers provided by plugins to be easily unit tested. It has no test framework dependencies so it can be used in Minitest, RSpec, or the testing framework of your choice. MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. For example: require \"tomo/testing\" def test_setup_directories tester = Tomo::Testing::MockPluginTester.new(settings: { deploy_to: \"/app\" }) tester.run_task(\"core:setup_directories\") assert_equal(\"mkdir -p /app /app/releases /app/shared\", tester.executed_script) end You can change the default mocking behavior by using mock_script_result , like this: require \"tomo/testing\" def test_install tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts ) end Every MockPluginTester instance loads a fresh, independent tomo environment, so mocks, plugins, settings, etc. specified in one tester will not affect any other tests. Note that you must require \"tomo/testing\" to use MockPluginTester.","title":"Tomo::Testing::MockPluginTester"},{"location":"api/testing/MockPluginTester/#class-methods","text":"","title":"Class methods"},{"location":"api/testing/MockPluginTester/#newplugin_names-settings-release-new_tester","text":"Build a new MockPluginTester that loads the given list of plugin_names . The resulting tester object can be used to simulate any tasks or helpers that are provided by these plugins. Note that the \u201ccore\u201d plugin is always loaded implicitly and does not need to be specified. Any settings that are specified will be applied after the defaults settings provided by the plugins have been defined. These settings can use template strings just like set . require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"puma\", settings: { application: \"test\", current_path: \"/app/current\" } ) Any release data specified will be available to the task under test via remote.release .","title":"new(*plugin_names, settings: {}, release: {}) \u2192 new_tester"},{"location":"api/testing/MockPluginTester/#instance-methods","text":"","title":"Instance methods"},{"location":"api/testing/MockPluginTester/#run_tasktask-args-nil","text":"Run the given task by its fully qualified name (the namespace is required). Any args , if specified, are passed to the task via settings[:run_args] . Any remote SSH scripts run by the task (e.g. via remote.run ) will be mocked according to rules previously supplied to mock_script_result . If a mock result has not been explicitly supplied, the script will use a default mock that returns a successful result with no output. require \"tomo/testing\" tester = Tomo::Testing::MockPluginTester.new( \"bundler\", settings: { release_path: \"/app/release\" } ) tester.mock_script_result(/bundle check/, exit_status: 1) tester.run_task(\"bundler:install\") assert_equal( [ \"cd /app/release && bundle check\", \"cd /app/release && bundle install\" ], tester.executed_scripts )","title":"run_task(task, *args) \u2192 nil"},{"location":"api/testing/MockPluginTester/#call_helperhelper-args-kwargs-obj","text":"Invoke the specified helper method name with the optional positional args and keyword kwargs . Returns the return value of the helper. Remote SSH scripts are mocked as explained in run_task . require \"tomo/testing\" def test_capture_returns_stdout_not_stderr tester = Tomo::Testing::MockPluginTester.new tester.mock_script_result(stderr: \"oh no\", stdout: \"hello world\\n\") captured = tester.call_helper(:capture, \"greet\") assert_equal(\"hello world\\n\", captured) end","title":"call_helper(helper, *args, **kwargs) \u2192 obj"},{"location":"api/testing/MockPluginTester/#mock_script_resultscript-stdout-stderr-exit_status-0-self","text":"Mock the return value of remote SSH scripts that match the given script . If script is a String, the mock rule will apply only to scripts that match this String exactly. If script is Regexp, the mock rule will apply to any scripts that match that pattern. If script is omitted, the mock rule will apply always. In this example, any task or helper invoked via this tester that runs readline /app/current will receive the given mock stdout response: tester.mock_script_result(\"readlink /app/current\", stdout: <<~OUT) /app/releases/20190420203028 OUT Here, any script that includes systemctl will fail with an exit status of 1: tester.mock_script_result(/systemctl/, exit_status: 1) This mocks all scripts to fail with exit status of 255 and stderr of \u201coh no!\u201d: tester.mock_script_result(exit_status: 255, stderr: \"oh no!\")","title":"mock_script_result(script=/.*/, stdout: “”, stderr: “”, exit_status: 0) \u2192 self"},{"location":"api/testing/MockPluginTester/#executed_script-string","text":"The remote SSH script that was run by a previous invocation of run_task or call_helper . If no script was run, then nil is returned. If more that one script was run, this will raise a RuntimeError .","title":"executed_script \u2192 String"},{"location":"api/testing/MockPluginTester/#executed_scripts-string","text":"All remote SSH scripts that were run by a previous invocation of run_task or call_helper . If no script was run, then an empty array is returned.","title":"executed_scripts \u2192 [String]"},{"location":"api/testing/MockPluginTester/#stdout-string","text":"Everything that was written to stdout during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned.","title":"stdout \u2192 String"},{"location":"api/testing/MockPluginTester/#stderr-string","text":"Everything that was written to stderr during the most recent invocation of run_task or call_helper . If nothing was written, then the empty string is returned.","title":"stderr \u2192 String"},{"location":"commands/deploy/","text":"deploy Deploy the current project to remote host(s). Usage $ tomo deploy [--dry-run] [options] Sequentially run the deploy list of tasks specified in .tomo/config.rb to deploy the project to a remote host. In practice, a deploy will usually consist of the following steps: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) During a deploy, tomo will initialize the :release_path setting based on the current date and time (e.g. /var/www/my-app/releases/20190616214752 ). Any tasks that copy files into a release ( git:create_release ), or run inside the release ( bundler:install , rails:assets_precompile , rails:db_migrate , etc.) will operate using this path. As a result, every tomo deploy will create a new entry in the releases directory, and the current symlink will point to the release that is currently active, i.e. the most recent successful deploy. The directory structure on the remote host looks like this: /var/www/my-app \u251c\u2500\u2500 git_repo/ \u251c\u2500\u2500 releases/ | \u251c\u2500\u2500 20190614192115/ | \u251c\u2500\u2500 20190615034736/ | \u2514\u2500\u2500 20190616214752/ \u251c\u2500\u2500 shared/ | \u251c\u2500\u2500 bundle/ | \u251c\u2500\u2500 log/ | \u251c\u2500\u2500 node_modules/ | \u2514\u2500\u2500 public/ | \u2514\u2500\u2500 assets/ \u251c\u2500\u2500 current -> /var/www/my-app/releases/20190616214752 \u2514\u2500\u2500 revisions.log This structure is customizable; see the core plugin settings for details. Tip: run tomo deploy --debug --dry-run to see an in-depth explanation of the settings and execution plan that will be used for the deployment. Options Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example Given the following configuration: host \"deployer@localhost\", port: 32809 deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end Then a deploy would produce: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:update \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /var/www/rails-new/releases/20191019200551 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /var/www/rails-new/releases/20191019200551 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/public /var/www/rails-new/releases/20191019200551/tmp cd /var/www/rails-new/releases/20191019200551 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /var/www/rails-new/releases/20191019200551/log ln -sf /var/www/rails-new/shared/node_modules /var/www/rails-new/releases/20191019200551/node_modules ln -sf /var/www/rails-new/shared/public/assets /var/www/rails-new/releases/20191019200551/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /var/www/rails-new/releases/20191019200551/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /var/www/rails-new/releases/20191019200551/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/tmp/sockets \u2022 core:write_release_json Writing 299 bytes to /var/www/rails-new/releases/20191019200551/.tomo_release.json \u2022 bundler:install cd /var/www/rails-new/releases/20191019200551 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The Gemfile's dependencies are satisfied \u2022 rails:db_migrate cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:migrate \u2022 rails:db_seed cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:seed \u2022 rails:assets_precompile cd /var/www/rails-new/releases/20191019200551 && bundle exec rails assets:precompile yarn install v1.16.0 [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@1.2.9: The platform \"linux\" is incompatible with this module. info \"fsevents@1.2.9\" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... warning \" > webpack-dev-server@3.8.1\" has unmet peer dependency \"webpack@^4.0.0\". warning \"webpack-dev-server > webpack-dev-middleware@3.7.2\" has unmet peer dependency \"webpack@^4.0.0\". [4/4] Building fresh packages... Done in 30.68s. I, [2019-10-19T20:06:30.645395 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css I, [2019-10-19T20:06:30.645832 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz Compiling\u2026 Compiled all packs in /var/www/rails-new/releases/20191019200551/public/packs WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option. You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands: npm install --save core-js@2 npm install --save core-js@3 yarn add core-js@2 yarn add core-js@3 \u2022 core:symlink_current ln -sf /var/www/rails-new/releases/20191019200551 /var/www/rails-new/current-0d3d2f7a6648294a mv -fT /var/www/rails-new/current-0d3d2f7a6648294a /var/www/rails-new/current \u2022 puma:restart systemctl --user start puma_rails-new.socket systemctl --user restart puma_rails-new.service \u2022 puma:check_active Checking if puma is active and listening on port 3000... systemctl --user is-active puma_rails-new.service curl -sS --connect-timeout 1 --max-time 10 http://localhost:3000 > /dev/null systemctl --user status puma_rails-new.service \u25cf puma_rails-new.service Loaded: loaded (enabled; vendor preset: enabled) Active: active (running) \u2022 core:clean_releases readlink /var/www/rails-new/current cd /var/www/rails-new/releases && ls -A1 \u2022 bundler:clean cd /var/www/rails-new/releases/20191019200551 && bundle clean The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. \u2022 core:log_revision Writing 100 bytes to /var/www/rails-new/revisions.log \u2714 Deployed rails-new to deployer@localhost:32829","title":"deploy"},{"location":"commands/deploy/#deploy","text":"Deploy the current project to remote host(s).","title":"deploy"},{"location":"commands/deploy/#usage","text":"$ tomo deploy [--dry-run] [options] Sequentially run the deploy list of tasks specified in .tomo/config.rb to deploy the project to a remote host. In practice, a deploy will usually consist of the following steps: Create a release (using the git:create_release task) Build the project (e.g. bundler:install , rails:assets_precompile ) Migrate data to the meet the requirements of the new release (e.g. rails:db_migrate ) Make the new release the \u201ccurrent\u201d one ( core:symlink_current ) Restart the app to use the new current release (e.g. puma:restart ) Perform any cleanup (e.g. bundler:clean ) During a deploy, tomo will initialize the :release_path setting based on the current date and time (e.g. /var/www/my-app/releases/20190616214752 ). Any tasks that copy files into a release ( git:create_release ), or run inside the release ( bundler:install , rails:assets_precompile , rails:db_migrate , etc.) will operate using this path. As a result, every tomo deploy will create a new entry in the releases directory, and the current symlink will point to the release that is currently active, i.e. the most recent successful deploy. The directory structure on the remote host looks like this: /var/www/my-app \u251c\u2500\u2500 git_repo/ \u251c\u2500\u2500 releases/ | \u251c\u2500\u2500 20190614192115/ | \u251c\u2500\u2500 20190615034736/ | \u2514\u2500\u2500 20190616214752/ \u251c\u2500\u2500 shared/ | \u251c\u2500\u2500 bundle/ | \u251c\u2500\u2500 log/ | \u251c\u2500\u2500 node_modules/ | \u2514\u2500\u2500 public/ | \u2514\u2500\u2500 assets/ \u251c\u2500\u2500 current -> /var/www/my-app/releases/20190616214752 \u2514\u2500\u2500 revisions.log This structure is customizable; see the core plugin settings for details. Tip: run tomo deploy --debug --dry-run to see an in-depth explanation of the settings and execution plan that will be used for the deployment.","title":"Usage"},{"location":"commands/deploy/#options","text":"Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/deploy/#example","text":"Given the following configuration: host \"deployer@localhost\", port: 32809 deploy do run \"env:update\" run \"git:create_release\" run \"core:symlink_shared\" run \"core:write_release_json\" run \"bundler:install\" run \"rails:db_migrate\" run \"rails:db_seed\" run \"rails:assets_precompile\" run \"core:symlink_current\" run \"puma:restart\" run \"puma:check_active\" run \"core:clean_releases\" run \"bundler:clean\" run \"core:log_revision\" end Then a deploy would produce: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:update \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /var/www/rails-new/releases/20191019200551 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /var/www/rails-new/releases/20191019200551 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/public /var/www/rails-new/releases/20191019200551/tmp cd /var/www/rails-new/releases/20191019200551 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /var/www/rails-new/releases/20191019200551/log ln -sf /var/www/rails-new/shared/node_modules /var/www/rails-new/releases/20191019200551/node_modules ln -sf /var/www/rails-new/shared/public/assets /var/www/rails-new/releases/20191019200551/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /var/www/rails-new/releases/20191019200551/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /var/www/rails-new/releases/20191019200551/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /var/www/rails-new/releases/20191019200551/tmp/sockets \u2022 core:write_release_json Writing 299 bytes to /var/www/rails-new/releases/20191019200551/.tomo_release.json \u2022 bundler:install cd /var/www/rails-new/releases/20191019200551 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The Gemfile's dependencies are satisfied \u2022 rails:db_migrate cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:migrate \u2022 rails:db_seed cd /var/www/rails-new/releases/20191019200551 && bundle exec rails db:seed \u2022 rails:assets_precompile cd /var/www/rails-new/releases/20191019200551 && bundle exec rails assets:precompile yarn install v1.16.0 [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@1.2.9: The platform \"linux\" is incompatible with this module. info \"fsevents@1.2.9\" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... warning \" > webpack-dev-server@3.8.1\" has unmet peer dependency \"webpack@^4.0.0\". warning \"webpack-dev-server > webpack-dev-middleware@3.7.2\" has unmet peer dependency \"webpack@^4.0.0\". [4/4] Building fresh packages... Done in 30.68s. I, [2019-10-19T20:06:30.645395 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css I, [2019-10-19T20:06:30.645832 #33263] INFO -- : Writing /var/www/rails-new/releases/20191019200551/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz Compiling\u2026 Compiled all packs in /var/www/rails-new/releases/20191019200551/public/packs WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option. You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands: npm install --save core-js@2 npm install --save core-js@3 yarn add core-js@2 yarn add core-js@3 \u2022 core:symlink_current ln -sf /var/www/rails-new/releases/20191019200551 /var/www/rails-new/current-0d3d2f7a6648294a mv -fT /var/www/rails-new/current-0d3d2f7a6648294a /var/www/rails-new/current \u2022 puma:restart systemctl --user start puma_rails-new.socket systemctl --user restart puma_rails-new.service \u2022 puma:check_active Checking if puma is active and listening on port 3000... systemctl --user is-active puma_rails-new.service curl -sS --connect-timeout 1 --max-time 10 http://localhost:3000 > /dev/null systemctl --user status puma_rails-new.service \u25cf puma_rails-new.service Loaded: loaded (enabled; vendor preset: enabled) Active: active (running) \u2022 core:clean_releases readlink /var/www/rails-new/current cd /var/www/rails-new/releases && ls -A1 \u2022 bundler:clean cd /var/www/rails-new/releases/20191019200551 && bundle clean The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. \u2022 core:log_revision Writing 100 bytes to /var/www/rails-new/revisions.log \u2714 Deployed rails-new to deployer@localhost:32829","title":"Example"},{"location":"commands/init/","text":"init Start a new tomo project with a sample config. Usage $ tomo init [APP] Set up a new tomo project named APP . If APP is not specified, the name of the current directory will be used. This command creates a .tomo/config.rb file relative the current directory containing some example configuration. Refer to Configuration for a detailed explanation of this file. tomo init will make educated guesses about your project and fill in some configuration settings for you: nodenv_node_version based on node --version nodenv_install_yarn based on whether yarn is present git_url based on metadata in .git/ for this project, if present rbenv_ruby_version based on the version of Ruby being used to run tomo Options Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example $ cd my-rails-app $ tomo init \u2714 Created .tomo/config.rb","title":"init"},{"location":"commands/init/#init","text":"Start a new tomo project with a sample config.","title":"init"},{"location":"commands/init/#usage","text":"$ tomo init [APP] Set up a new tomo project named APP . If APP is not specified, the name of the current directory will be used. This command creates a .tomo/config.rb file relative the current directory containing some example configuration. Refer to Configuration for a detailed explanation of this file. tomo init will make educated guesses about your project and fill in some configuration settings for you: nodenv_node_version based on node --version nodenv_install_yarn based on whether yarn is present git_url based on metadata in .git/ for this project, if present rbenv_ruby_version based on the version of Ruby being used to run tomo","title":"Usage"},{"location":"commands/init/#options","text":"Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/init/#example","text":"$ cd my-rails-app $ tomo init \u2714 Created .tomo/config.rb","title":"Example"},{"location":"commands/run/","text":"run Run a specific remote task from the current project. Usage $ tomo run [--dry-run] [options] [--] TASK [ARGS...] Remotely run one specified TASK, optionally passing ARGS to that task. For example, if this project uses the rails plugin , you could run: $ tomo run -- rails:console --sandbox This will run the rails:console task on the host specified in .tomo/config.rb configuration file , and will pass the --sandbox argument to that task. The -- is used to separate tomo options from options that are passed to the task. If a task does not accept options, the -- can be omitted, like this: $ tomo run core:clean_releases When you specify a task name, the run command is implied and can be omitted, so this works as well: $ tomo core:clean_releases You can run any task defined by plugins loaded by the plugin declarations in .tomo/config.rb . To see a list of available tasks, run the tasks command. During the run command, tomo will initialize the :release_path setting to be equal to the current symlink (i.e. /var/www/my-app/current ). This means that the task will run within the current release. Options Option Purpose --[no-]privileged Run the task using a privileged user (e.g. root). This user is configured per host . -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example Given the following configuration: plugin \"puma\" host \"deployer@app.example.com\" Then we could run puma:restart like this: $ tomo run puma:restart tomo run v1.1.2 \u2192 Connecting to deployer@app.example.com \u2022 puma:restart systemctl --user start puma_example.socket systemctl --user restart puma_example.service \u2714 Ran puma:restart on deployer@app.example.com","title":"run"},{"location":"commands/run/#run","text":"Run a specific remote task from the current project.","title":"run"},{"location":"commands/run/#usage","text":"$ tomo run [--dry-run] [options] [--] TASK [ARGS...] Remotely run one specified TASK, optionally passing ARGS to that task. For example, if this project uses the rails plugin , you could run: $ tomo run -- rails:console --sandbox This will run the rails:console task on the host specified in .tomo/config.rb configuration file , and will pass the --sandbox argument to that task. The -- is used to separate tomo options from options that are passed to the task. If a task does not accept options, the -- can be omitted, like this: $ tomo run core:clean_releases When you specify a task name, the run command is implied and can be omitted, so this works as well: $ tomo core:clean_releases You can run any task defined by plugins loaded by the plugin declarations in .tomo/config.rb . To see a list of available tasks, run the tasks command. During the run command, tomo will initialize the :release_path setting to be equal to the current symlink (i.e. /var/www/my-app/current ). This means that the task will run within the current release.","title":"Usage"},{"location":"commands/run/#options","text":"Option Purpose --[no-]privileged Run the task using a privileged user (e.g. root). This user is configured per host . -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/run/#example","text":"Given the following configuration: plugin \"puma\" host \"deployer@app.example.com\" Then we could run puma:restart like this: $ tomo run puma:restart tomo run v1.1.2 \u2192 Connecting to deployer@app.example.com \u2022 puma:restart systemctl --user start puma_example.socket systemctl --user restart puma_example.service \u2714 Ran puma:restart on deployer@app.example.com","title":"Example"},{"location":"commands/setup/","text":"setup Prepare the current project for its first deploy. Usage $ tomo setup [--dry-run] [options] Prepare the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. During setup, tomo will initialize the :release_path setting to be a temporary directory based on the current date and time (e.g. /tmp/tomo-a4DBHX0P/20190616214752 ). This means setup tasks (e.g. rails:db_create , rails:db_schema_load ) run in a location that won\u2019t be deployed as an actual release. Options Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example Given the following configuration: host \"deployer@localhost\", port: 32829 setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Then a setup would produce: $ tomo setup tomo setup v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:setup Writing 314 bytes to /var/www/rails-new/envrc cat .bashrc Writing 3845 bytes to .bashrc \u2022 core:setup_directories mkdir -p /var/www/rails-new /var/www/rails-new/releases /var/www/rails-new/shared \u2022 git:clone [ -d /var/www/rails-new/git_repo ] mkdir -p /var/www/rails-new export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git clone --mirror git@github.com:mattbrictson/rails-new.git /var/www/rails-new/git_repo Cloning into bare repository '/var/www/rails-new/git_repo'... Warning: Permanently added 'github.com,192.30.255.113' (RSA) to the list of known hosts. \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /tmp/tomo-a4DBHX0P/20191019200138 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /tmp/tomo-a4DBHX0P/20191019200138 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/public /tmp/tomo-a4DBHX0P/20191019200138/tmp cd /tmp/tomo-a4DBHX0P/20191019200138 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /tmp/tomo-a4DBHX0P/20191019200138/log ln -sf /var/www/rails-new/shared/node_modules /tmp/tomo-a4DBHX0P/20191019200138/node_modules ln -sf /var/www/rails-new/shared/public/assets /tmp/tomo-a4DBHX0P/20191019200138/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /tmp/tomo-a4DBHX0P/20191019200138/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /tmp/tomo-a4DBHX0P/20191019200138/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/tmp/sockets \u2022 nodenv:install export PATH=$HOME/.nodenv/bin:$HOME/.nodenv/shims:$PATH && curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-installer | bash Installing nodenv with git... Initialized empty Git repository in /home/deployer/.nodenv/.git/ Updating origin From https://github.com/nodenv/nodenv * [new branch] master -> origin/master * [new tag] 0.2.0 -> 0.2.0 * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 * [new tag] v1.2.0 -> v1.2.0 * [new tag] v1.3.0 -> v1.3.0 Branch 'master' set up to track remote branch 'master' from 'origin'. Already on 'master' make: Entering directory '/home/deployer/.nodenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/nodenv-realpath.dylib -o ../libexec/nodenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.nodenv/src' Installing node-build with git... Cloning into '/home/deployer/.nodenv/plugins/node-build'... Running doctor script to verify installation... Checking for `nodenv' in PATH: /home/deployer/.nodenv/bin/nodenv Checking for nodenv shims in PATH: OK Checking `nodenv install' support: /home/deployer/.nodenv/plugins/node-build/bin/nodenv-install (node-build 4.6.4-9-g3a5ae01b) Counting installed Node versions: none There aren't any Node versions installed under `/home/deployer/.nodenv/versions'. You can install Node versions like so: nodenv install 2.2.4 Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.nodenv/bin' is added to PATH. 2. Run `nodenv init' to see instructions how to configure nodenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 3944 bytes to .bashrc nodenv versions nodenv install 10.16.0 Downloading node-v10.16.0-linux-x64.tar.gz... -> https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.gz Installing node-v10.16.0-linux-x64... Installed node-v10.16.0-linux-x64 to /home/deployer/.nodenv/versions/10.16.0 nodenv global 10.16.0 npm i -g yarn@1.16.0 /home/deployer/.nodenv/versions/10.16.0/bin/yarnpkg -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js /home/deployer/.nodenv/versions/10.16.0/bin/yarn -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js + yarn@1.16.0 added 1 package in 0.54s \u2022 rbenv:install export PATH=$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH && curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash Installing rbenv with git... Initialized empty Git repository in /home/deployer/.rbenv/.git/ Updating origin From https://github.com/rbenv/rbenv * [new branch] master -> origin/master * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.1.1 -> v0.1.1 * [new tag] v0.1.2 -> v0.1.2 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.2.1 -> v0.2.1 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 Already on 'master' Branch 'master' set up to track remote branch 'master' from 'origin'. make: Entering directory '/home/deployer/.rbenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/rbenv-realpath.dylib -o ../libexec/rbenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.rbenv/src' Installing ruby-build with git... Cloning into '/home/deployer/.rbenv/plugins/ruby-build'... Running doctor script to verify installation... Checking for `rbenv' in PATH: /home/deployer/.rbenv/bin/rbenv Checking for rbenv shims in PATH: OK Checking `rbenv install' support: /home/deployer/.rbenv/plugins/ruby-build/bin/rbenv-install (ruby-build 20191004) Counting installed Ruby versions: none There aren't any Ruby versions installed under `/home/deployer/.rbenv/versions'. You can install Ruby versions like so: rbenv install 2.2.4 Checking RubyGems settings: OK Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.rbenv/bin' is added to PATH. 2. Run `rbenv init' to see instructions how to configure rbenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 4041 bytes to .bashrc rbenv versions Installing ruby 2.6.5 -- this may take several minutes CFLAGS=-O3 rbenv install 2.6.5 Downloading ruby-2.6.5.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.5.tar.bz2 Installing ruby-2.6.5... Installed ruby-2.6.5 to /home/deployer/.rbenv/versions/2.6.5 rbenv global 2.6.5 \u2022 bundler:upgrade_bundler tail -n 10 /tmp/tomo-a4DBHX0P/20191019200138/Gemfile.lock gem install bundler --conservative --no-document -v 2.0.2 Successfully installed bundler-2.0.2 1 gem installed \u2022 bundler:config mkdir -p .bundle Writing 146 bytes to .bundle/config \u2022 bundler:install cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The following gems are missing * rake (13.0.0) * concurrent-ruby (1.1.5) * i18n (1.7.0) * minitest (5.12.2) * thread_safe (0.3.6) * tzinfo (1.2.5) * zeitwerk (2.2.0) * activesupport (6.0.0) * builder (3.2.3) * erubi (1.9.0) * mini_portile2 (2.4.0) * nokogiri (1.10.4) * rails-dom-testing (2.0.3) * crass (1.0.4) * loofah (2.3.0) * rails-html-sanitizer (1.3.0) * actionview (6.0.0) * rack (2.0.7) * rack-test (1.1.0) * actionpack (6.0.0) * nio4r (2.5.2) * websocket-extensions (0.1.4) * websocket-driver (0.7.1) * actioncable (6.0.0) * globalid (0.4.2) * activejob (6.0.0) * activemodel (6.0.0) * activerecord (6.0.0) * mimemagic (0.3.3) * marcel (0.3.3) * activestorage (6.0.0) * mini_mime (1.0.2) * mail (2.7.1) * actionmailbox (6.0.0) * actionmailer (6.0.0) * actiontext (6.0.0) * msgpack (1.3.1) * bootsnap (1.4.5) * ffi (1.11.1) * jbuilder (2.9.1) * method_source (0.9.2) * puma (3.12.1) * rack-proxy (0.6.5) * thor (0.20.3) * railties (6.0.0) * sprockets (3.7.2) * sprockets-rails (3.2.1) * rails (6.0.0) * rb-fsevent (0.10.3) * rb-inotify (0.10.0) * sass-listen (4.0.0) * sass (3.7.4) * tilt (2.0.10) * sass-rails (5.1.0) * sqlite3 (1.4.1) * turbolinks-source (5.2.0) * turbolinks (5.2.1) * webpacker (4.0.7) Install missing gems with `bundle install` cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle install The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching rake 13.0.0 Installing rake 13.0.0 Fetching thread_safe 0.3.6 Fetching concurrent-ruby 1.1.5 Fetching minitest 5.12.2 Installing minitest 5.12.2 Installing thread_safe 0.3.6 Installing concurrent-ruby 1.1.5 Fetching zeitwerk 2.2.0 Installing zeitwerk 2.2.0 Fetching builder 3.2.3 Fetching erubi 1.9.0 Installing builder 3.2.3 Installing erubi 1.9.0 Fetching mini_portile2 2.4.0 Installing mini_portile2 2.4.0 Fetching crass 1.0.4 Installing crass 1.0.4 Fetching rack 2.0.7 Installing rack 2.0.7 Fetching nio4r 2.5.2 Installing nio4r 2.5.2 with native extensions Fetching websocket-extensions 0.1.4 Installing websocket-extensions 0.1.4 Fetching mimemagic 0.3.3 Installing mimemagic 0.3.3 Fetching mini_mime 1.0.2 Fetching msgpack 1.3.1 Installing mini_mime 1.0.2 Using bundler 2.0.2 Fetching ffi 1.11.1 Installing msgpack 1.3.1 with native extensions Installing ffi 1.11.1 with native extensions Fetching method_source 0.9.2 Installing method_source 0.9.2 Fetching puma 3.12.1 Installing puma 3.12.1 with native extensions Fetching thor 0.20.3 Installing thor 0.20.3 Fetching rb-fsevent 0.10.3 Installing rb-fsevent 0.10.3 Fetching tilt 2.0.10 Installing tilt 2.0.10 Fetching sqlite3 1.4.1 Installing sqlite3 1.4.1 with native extensions Fetching turbolinks-source 5.2.0 Installing turbolinks-source 5.2.0 Fetching tzinfo 1.2.5 Installing tzinfo 1.2.5 Fetching nokogiri 1.10.4 Installing nokogiri 1.10.4 with native extensions Fetching i18n 1.7.0 Installing i18n 1.7.0 Fetching websocket-driver 0.7.1 Installing websocket-driver 0.7.1 with native extensions Fetching marcel 0.3.3 Installing marcel 0.3.3 Fetching rack-test 1.1.0 Installing rack-test 1.1.0 Fetching rack-proxy 0.6.5 Installing rack-proxy 0.6.5 Fetching sprockets 3.7.2 Installing sprockets 3.7.2 Fetching mail 2.7.1 Installing mail 2.7.1 Fetching bootsnap 1.4.5 Installing bootsnap 1.4.5 with native extensions Fetching rb-inotify 0.10.0 Installing rb-inotify 0.10.0 Fetching turbolinks 5.2.1 Installing turbolinks 5.2.1 Fetching activesupport 6.0.0 Installing activesupport 6.0.0 Fetching loofah 2.3.0 Installing loofah 2.3.0 Fetching sass-listen 4.0.0 Installing sass-listen 4.0.0 Fetching rails-html-sanitizer 1.3.0 Fetching sass 3.7.4 Fetching rails-dom-testing 2.0.3 Installing rails-html-sanitizer 1.3.0 Fetching globalid 0.4.2 Installing rails-dom-testing 2.0.3 Installing globalid 0.4.2 Installing sass 3.7.4 Fetching activemodel 6.0.0 Fetching jbuilder 2.9.1 Installing jbuilder 2.9.1 Installing activemodel 6.0.0 Fetching activejob 6.0.0 Installing activejob 6.0.0 Fetching actionview 6.0.0 Installing actionview 6.0.0 Fetching activerecord 6.0.0 Installing activerecord 6.0.0 Fetching actionpack 6.0.0 Installing actionpack 6.0.0 Fetching actioncable 6.0.0 Fetching actionmailer 6.0.0 Fetching railties 6.0.0 Installing actionmailer 6.0.0 Installing actioncable 6.0.0 Fetching sprockets-rails 3.2.1 Installing railties 6.0.0 Installing sprockets-rails 3.2.1 Fetching activestorage 6.0.0 Installing activestorage 6.0.0 Fetching actionmailbox 6.0.0 Fetching actiontext 6.0.0 Installing actionmailbox 6.0.0 Installing actiontext 6.0.0 Fetching sass-rails 5.1.0 Fetching rails 6.0.0 Fetching webpacker 4.0.7 Installing sass-rails 5.1.0 Installing rails 6.0.0 Installing webpacker 4.0.7 Bundle complete! 17 Gemfile dependencies, 59 gems now installed. Gems in the groups development and test were not installed. Bundled gems are installed into `/var/www/rails-new/shared/bundle` Post-install message from i18n: HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application. Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x. For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0 Post-install message from sass: Ruby Sass has reached end-of-life and should no longer be used. * If you use Sass as a command-line tool, we recommend using Dart Sass, the new primary implementation: https://sass-lang.com/install * If you use Sass as a plug-in for a Ruby web framework, we recommend using the sassc gem: https://github.com/sass/sassc-ruby#readme * For more details, please refer to the Sass blog: https://sass-lang.com/blog/posts/7828841 \u2022 rails:db_create cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:version Database already exists; skipping db:create. \u2022 rails:db_schema_load [ -f /tmp/tomo-a4DBHX0P/20191019200138/db/schema.rb ] WARNING: db/schema.rb is not present; skipping schema:load. \u2022 rails:db_seed cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:seed \u2022 puma:setup_systemd loginctl user-status deployer mkdir -p .config/systemd/user Writing 218 bytes to .config/systemd/user/puma_rails-new.socket Writing 570 bytes to .config/systemd/user/puma_rails-new.service systemctl --user daemon-reload systemctl --user enable puma_rails-new.service puma_rails-new.socket \u2714 Performed setup of rails-new on deployer@localhost:32829","title":"setup"},{"location":"commands/setup/#setup","text":"Prepare the current project for its first deploy.","title":"setup"},{"location":"commands/setup/#usage","text":"$ tomo setup [--dry-run] [options] Prepare the remote host for its first deploy by sequentially running the setup list of tasks specified in .tomo/config.rb . These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place. During setup, tomo will initialize the :release_path setting to be a temporary directory based on the current date and time (e.g. /tmp/tomo-a4DBHX0P/20190616214752 ). This means setup tasks (e.g. rails:db_create , rails:db_schema_load ) run in a location that won\u2019t be deployed as an actual release.","title":"Usage"},{"location":"commands/setup/#options","text":"Option Purpose -e ENVIRONMENT , --environment ENVIRONMENT If the configuration contains multiple environments , specify the ENVIRONMENT that should be used (e.g. production). The host(s) and settings can be different per environment. -s NAME=VALUE , --setting NAME=VALUE Override the setting NAME with the given VALUE. Refer to the settings overrides documentation for a detailed explanation. --[no-]dry-run Simulate running tasks instead of using real SSH. -c PATH , --config PATH Override the PATH where the configuration file is loaded from. (Default: .tomo/config.rb ). --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/setup/#example","text":"Given the following configuration: host \"deployer@localhost\", port: 32829 setup do run \"env:setup\" run \"core:setup_directories\" run \"git:config\" run \"git:clone\" run \"git:create_release\" run \"core:symlink_shared\" run \"nodenv:install\" run \"rbenv:install\" run \"bundler:upgrade_bundler\" run \"bundler:config\" run \"bundler:install\" run \"rails:db_create\" run \"rails:db_schema_load\" run \"rails:db_seed\" run \"puma:setup_systemd\" end Then a setup would produce: $ tomo setup tomo setup v1.0.0 \u2192 Connecting to deployer@localhost:32829 \u2022 env:setup Writing 314 bytes to /var/www/rails-new/envrc cat .bashrc Writing 3845 bytes to .bashrc \u2022 core:setup_directories mkdir -p /var/www/rails-new /var/www/rails-new/releases /var/www/rails-new/shared \u2022 git:clone [ -d /var/www/rails-new/git_repo ] mkdir -p /var/www/rails-new export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git clone --mirror git@github.com:mattbrictson/rails-new.git /var/www/rails-new/git_repo Cloning into bare repository '/var/www/rails-new/git_repo'... Warning: Permanently added 'github.com,192.30.255.113' (RSA) to the list of known hosts. \u2022 git:create_release cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git remote update --prune Fetching origin cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git log -n1 --date=iso --pretty=format:\"%H/%cd/%ae\" main -- Writing 60 bytes to /var/www/rails-new/git_repo/info/attributes mkdir -p /tmp/tomo-a4DBHX0P/20191019200138 cd /var/www/rails-new/git_repo && export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication\\=no\\ -o\\ StrictHostKeyChecking\\=no && git archive main | tar -x -f - -C /tmp/tomo-a4DBHX0P/20191019200138 \u2022 core:symlink_shared mkdir -p /var/www/rails-new/shared/log /var/www/rails-new/shared/node_modules /var/www/rails-new/shared/public/assets /var/www/rails-new/shared/tmp/cache /var/www/rails-new/shared/tmp/pids /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/public /tmp/tomo-a4DBHX0P/20191019200138/tmp cd /tmp/tomo-a4DBHX0P/20191019200138 && rm -rf log node_modules public/assets tmp/cache tmp/pids tmp/sockets ln -sf /var/www/rails-new/shared/log /tmp/tomo-a4DBHX0P/20191019200138/log ln -sf /var/www/rails-new/shared/node_modules /tmp/tomo-a4DBHX0P/20191019200138/node_modules ln -sf /var/www/rails-new/shared/public/assets /tmp/tomo-a4DBHX0P/20191019200138/public/assets ln -sf /var/www/rails-new/shared/tmp/cache /tmp/tomo-a4DBHX0P/20191019200138/tmp/cache ln -sf /var/www/rails-new/shared/tmp/pids /tmp/tomo-a4DBHX0P/20191019200138/tmp/pids ln -sf /var/www/rails-new/shared/tmp/sockets /tmp/tomo-a4DBHX0P/20191019200138/tmp/sockets \u2022 nodenv:install export PATH=$HOME/.nodenv/bin:$HOME/.nodenv/shims:$PATH && curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-installer | bash Installing nodenv with git... Initialized empty Git repository in /home/deployer/.nodenv/.git/ Updating origin From https://github.com/nodenv/nodenv * [new branch] master -> origin/master * [new tag] 0.2.0 -> 0.2.0 * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 * [new tag] v1.2.0 -> v1.2.0 * [new tag] v1.3.0 -> v1.3.0 Branch 'master' set up to track remote branch 'master' from 'origin'. Already on 'master' make: Entering directory '/home/deployer/.nodenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/nodenv-realpath.dylib -o ../libexec/nodenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.nodenv/src' Installing node-build with git... Cloning into '/home/deployer/.nodenv/plugins/node-build'... Running doctor script to verify installation... Checking for `nodenv' in PATH: /home/deployer/.nodenv/bin/nodenv Checking for nodenv shims in PATH: OK Checking `nodenv install' support: /home/deployer/.nodenv/plugins/node-build/bin/nodenv-install (node-build 4.6.4-9-g3a5ae01b) Counting installed Node versions: none There aren't any Node versions installed under `/home/deployer/.nodenv/versions'. You can install Node versions like so: nodenv install 2.2.4 Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.nodenv/bin' is added to PATH. 2. Run `nodenv init' to see instructions how to configure nodenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 3944 bytes to .bashrc nodenv versions nodenv install 10.16.0 Downloading node-v10.16.0-linux-x64.tar.gz... -> https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.gz Installing node-v10.16.0-linux-x64... Installed node-v10.16.0-linux-x64 to /home/deployer/.nodenv/versions/10.16.0 nodenv global 10.16.0 npm i -g yarn@1.16.0 /home/deployer/.nodenv/versions/10.16.0/bin/yarnpkg -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js /home/deployer/.nodenv/versions/10.16.0/bin/yarn -> /home/deployer/.nodenv/versions/10.16.0/lib/node_modules/yarn/bin/yarn.js + yarn@1.16.0 added 1 package in 0.54s \u2022 rbenv:install export PATH=$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH && curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash Installing rbenv with git... Initialized empty Git repository in /home/deployer/.rbenv/.git/ Updating origin From https://github.com/rbenv/rbenv * [new branch] master -> origin/master * [new tag] v0.1.0 -> v0.1.0 * [new tag] v0.1.1 -> v0.1.1 * [new tag] v0.1.2 -> v0.1.2 * [new tag] v0.2.0 -> v0.2.0 * [new tag] v0.2.1 -> v0.2.1 * [new tag] v0.3.0 -> v0.3.0 * [new tag] v0.4.0 -> v0.4.0 * [new tag] v1.0.0 -> v1.0.0 * [new tag] v1.1.0 -> v1.1.0 * [new tag] v1.1.1 -> v1.1.1 * [new tag] v1.1.2 -> v1.1.2 Already on 'master' Branch 'master' set up to track remote branch 'master' from 'origin'. make: Entering directory '/home/deployer/.rbenv/src' gcc -fPIC -c -o realpath.o realpath.c gcc -shared -Wl,-soname,../libexec/rbenv-realpath.dylib -o ../libexec/rbenv-realpath.dylib realpath.o make: Leaving directory '/home/deployer/.rbenv/src' Installing ruby-build with git... Cloning into '/home/deployer/.rbenv/plugins/ruby-build'... Running doctor script to verify installation... Checking for `rbenv' in PATH: /home/deployer/.rbenv/bin/rbenv Checking for rbenv shims in PATH: OK Checking `rbenv install' support: /home/deployer/.rbenv/plugins/ruby-build/bin/rbenv-install (ruby-build 20191004) Counting installed Ruby versions: none There aren't any Ruby versions installed under `/home/deployer/.rbenv/versions'. You can install Ruby versions like so: rbenv install 2.2.4 Checking RubyGems settings: OK Auditing installed plugins: OK All done! Note that this installer doesn't yet configure your shell startup files: 1. You'll want to ensure that `~/.rbenv/bin' is added to PATH. 2. Run `rbenv init' to see instructions how to configure rbenv for your shell. 3. Launch a new terminal window to verify that the configuration is correct. cat .bashrc Writing 4041 bytes to .bashrc rbenv versions Installing ruby 2.6.5 -- this may take several minutes CFLAGS=-O3 rbenv install 2.6.5 Downloading ruby-2.6.5.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.5.tar.bz2 Installing ruby-2.6.5... Installed ruby-2.6.5 to /home/deployer/.rbenv/versions/2.6.5 rbenv global 2.6.5 \u2022 bundler:upgrade_bundler tail -n 10 /tmp/tomo-a4DBHX0P/20191019200138/Gemfile.lock gem install bundler --conservative --no-document -v 2.0.2 Successfully installed bundler-2.0.2 1 gem installed \u2022 bundler:config mkdir -p .bundle Writing 146 bytes to .bundle/config \u2022 bundler:install cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle check The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. The following gems are missing * rake (13.0.0) * concurrent-ruby (1.1.5) * i18n (1.7.0) * minitest (5.12.2) * thread_safe (0.3.6) * tzinfo (1.2.5) * zeitwerk (2.2.0) * activesupport (6.0.0) * builder (3.2.3) * erubi (1.9.0) * mini_portile2 (2.4.0) * nokogiri (1.10.4) * rails-dom-testing (2.0.3) * crass (1.0.4) * loofah (2.3.0) * rails-html-sanitizer (1.3.0) * actionview (6.0.0) * rack (2.0.7) * rack-test (1.1.0) * actionpack (6.0.0) * nio4r (2.5.2) * websocket-extensions (0.1.4) * websocket-driver (0.7.1) * actioncable (6.0.0) * globalid (0.4.2) * activejob (6.0.0) * activemodel (6.0.0) * activerecord (6.0.0) * mimemagic (0.3.3) * marcel (0.3.3) * activestorage (6.0.0) * mini_mime (1.0.2) * mail (2.7.1) * actionmailbox (6.0.0) * actionmailer (6.0.0) * actiontext (6.0.0) * msgpack (1.3.1) * bootsnap (1.4.5) * ffi (1.11.1) * jbuilder (2.9.1) * method_source (0.9.2) * puma (3.12.1) * rack-proxy (0.6.5) * thor (0.20.3) * railties (6.0.0) * sprockets (3.7.2) * sprockets-rails (3.2.1) * rails (6.0.0) * rb-fsevent (0.10.3) * rb-inotify (0.10.0) * sass-listen (4.0.0) * sass (3.7.4) * tilt (2.0.10) * sass-rails (5.1.0) * sqlite3 (1.4.1) * turbolinks-source (5.2.0) * turbolinks (5.2.1) * webpacker (4.0.7) Install missing gems with `bundle install` cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle install The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching rake 13.0.0 Installing rake 13.0.0 Fetching thread_safe 0.3.6 Fetching concurrent-ruby 1.1.5 Fetching minitest 5.12.2 Installing minitest 5.12.2 Installing thread_safe 0.3.6 Installing concurrent-ruby 1.1.5 Fetching zeitwerk 2.2.0 Installing zeitwerk 2.2.0 Fetching builder 3.2.3 Fetching erubi 1.9.0 Installing builder 3.2.3 Installing erubi 1.9.0 Fetching mini_portile2 2.4.0 Installing mini_portile2 2.4.0 Fetching crass 1.0.4 Installing crass 1.0.4 Fetching rack 2.0.7 Installing rack 2.0.7 Fetching nio4r 2.5.2 Installing nio4r 2.5.2 with native extensions Fetching websocket-extensions 0.1.4 Installing websocket-extensions 0.1.4 Fetching mimemagic 0.3.3 Installing mimemagic 0.3.3 Fetching mini_mime 1.0.2 Fetching msgpack 1.3.1 Installing mini_mime 1.0.2 Using bundler 2.0.2 Fetching ffi 1.11.1 Installing msgpack 1.3.1 with native extensions Installing ffi 1.11.1 with native extensions Fetching method_source 0.9.2 Installing method_source 0.9.2 Fetching puma 3.12.1 Installing puma 3.12.1 with native extensions Fetching thor 0.20.3 Installing thor 0.20.3 Fetching rb-fsevent 0.10.3 Installing rb-fsevent 0.10.3 Fetching tilt 2.0.10 Installing tilt 2.0.10 Fetching sqlite3 1.4.1 Installing sqlite3 1.4.1 with native extensions Fetching turbolinks-source 5.2.0 Installing turbolinks-source 5.2.0 Fetching tzinfo 1.2.5 Installing tzinfo 1.2.5 Fetching nokogiri 1.10.4 Installing nokogiri 1.10.4 with native extensions Fetching i18n 1.7.0 Installing i18n 1.7.0 Fetching websocket-driver 0.7.1 Installing websocket-driver 0.7.1 with native extensions Fetching marcel 0.3.3 Installing marcel 0.3.3 Fetching rack-test 1.1.0 Installing rack-test 1.1.0 Fetching rack-proxy 0.6.5 Installing rack-proxy 0.6.5 Fetching sprockets 3.7.2 Installing sprockets 3.7.2 Fetching mail 2.7.1 Installing mail 2.7.1 Fetching bootsnap 1.4.5 Installing bootsnap 1.4.5 with native extensions Fetching rb-inotify 0.10.0 Installing rb-inotify 0.10.0 Fetching turbolinks 5.2.1 Installing turbolinks 5.2.1 Fetching activesupport 6.0.0 Installing activesupport 6.0.0 Fetching loofah 2.3.0 Installing loofah 2.3.0 Fetching sass-listen 4.0.0 Installing sass-listen 4.0.0 Fetching rails-html-sanitizer 1.3.0 Fetching sass 3.7.4 Fetching rails-dom-testing 2.0.3 Installing rails-html-sanitizer 1.3.0 Fetching globalid 0.4.2 Installing rails-dom-testing 2.0.3 Installing globalid 0.4.2 Installing sass 3.7.4 Fetching activemodel 6.0.0 Fetching jbuilder 2.9.1 Installing jbuilder 2.9.1 Installing activemodel 6.0.0 Fetching activejob 6.0.0 Installing activejob 6.0.0 Fetching actionview 6.0.0 Installing actionview 6.0.0 Fetching activerecord 6.0.0 Installing activerecord 6.0.0 Fetching actionpack 6.0.0 Installing actionpack 6.0.0 Fetching actioncable 6.0.0 Fetching actionmailer 6.0.0 Fetching railties 6.0.0 Installing actionmailer 6.0.0 Installing actioncable 6.0.0 Fetching sprockets-rails 3.2.1 Installing railties 6.0.0 Installing sprockets-rails 3.2.1 Fetching activestorage 6.0.0 Installing activestorage 6.0.0 Fetching actionmailbox 6.0.0 Fetching actiontext 6.0.0 Installing actionmailbox 6.0.0 Installing actiontext 6.0.0 Fetching sass-rails 5.1.0 Fetching rails 6.0.0 Fetching webpacker 4.0.7 Installing sass-rails 5.1.0 Installing rails 6.0.0 Installing webpacker 4.0.7 Bundle complete! 17 Gemfile dependencies, 59 gems now installed. Gems in the groups development and test were not installed. Bundled gems are installed into `/var/www/rails-new/shared/bundle` Post-install message from i18n: HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application. Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x. For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0 Post-install message from sass: Ruby Sass has reached end-of-life and should no longer be used. * If you use Sass as a command-line tool, we recommend using Dart Sass, the new primary implementation: https://sass-lang.com/install * If you use Sass as a plug-in for a Ruby web framework, we recommend using the sassc gem: https://github.com/sass/sassc-ruby#readme * For more details, please refer to the Sass blog: https://sass-lang.com/blog/posts/7828841 \u2022 rails:db_create cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:version Database already exists; skipping db:create. \u2022 rails:db_schema_load [ -f /tmp/tomo-a4DBHX0P/20191019200138/db/schema.rb ] WARNING: db/schema.rb is not present; skipping schema:load. \u2022 rails:db_seed cd /tmp/tomo-a4DBHX0P/20191019200138 && bundle exec rails db:seed \u2022 puma:setup_systemd loginctl user-status deployer mkdir -p .config/systemd/user Writing 218 bytes to .config/systemd/user/puma_rails-new.socket Writing 570 bytes to .config/systemd/user/puma_rails-new.service systemctl --user daemon-reload systemctl --user enable puma_rails-new.service puma_rails-new.socket \u2714 Performed setup of rails-new on deployer@localhost:32829","title":"Example"},{"location":"commands/tasks/","text":"tasks List all tasks that can be used with the run command. Usage $ tomo tasks List all tomo tasks (i.e. those that can be used with tomo run ). Available tasks are those defined by plugins loaded in .tomo/config.rb . Refer to the Configuration guide for an explanation of how plugins are loaded. The reference documentation for each plugin (e.g. core , git ) describes the tasks these plugins provide. Options Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page. Example $ tomo tasks bundler:clean bundler:install bundler:upgrade_bundler core:clean_releases core:log_revision core:setup_directories core:symlink_current core:symlink_shared core:write_release_json env:set env:setup env:show env:unset env:update git:clone git:create_release nodenv:install puma:restart rails:assets_precompile rails:console rails:db_create rails:db_migrate rails:db_schema_load rails:db_seed rails:db_setup rails:db_structure_load rbenv:install","title":"tasks"},{"location":"commands/tasks/#tasks","text":"List all tasks that can be used with the run command.","title":"tasks"},{"location":"commands/tasks/#usage","text":"$ tomo tasks List all tomo tasks (i.e. those that can be used with tomo run ). Available tasks are those defined by plugins loaded in .tomo/config.rb . Refer to the Configuration guide for an explanation of how plugins are loaded. The reference documentation for each plugin (e.g. core , git ) describes the tasks these plugins provide.","title":"Usage"},{"location":"commands/tasks/#options","text":"Option Purpose --[no-]color By default, tomo automatically determines whether to use color output based on the capabilities of the terminal. Use this option to override this behavior and force tomo to enable or disable color output. --[no-]debug Enables verbose logging output that is helpful for troubleshooting. This includes runtime information such as the environment variables on the remote host and all tomo settings. Each SSH command executed by tomo is also logged in detail. --[no-]trace Normally if a tomo command fails, a concise and helpful error message is printed. If --trace is specified, tomo will also print the full backtrace. -h , --help Prints the help for this tomo command, similar to what you see on this page.","title":"Options"},{"location":"commands/tasks/#example","text":"$ tomo tasks bundler:clean bundler:install bundler:upgrade_bundler core:clean_releases core:log_revision core:setup_directories core:symlink_current core:symlink_shared core:write_release_json env:set env:setup env:show env:unset env:update git:clone git:create_release nodenv:install puma:restart rails:assets_precompile rails:console rails:db_create rails:db_migrate rails:db_schema_load rails:db_seed rails:db_setup rails:db_structure_load rbenv:install","title":"Example"},{"location":"plugins/bundler/","text":"bundler The bundler plugin installs ruby gem dependencies using bundler. This is required for deploying Rails apps. It also provides conveniences for using bundle exec . Settings Note that the settings listed here only take effect if you run the bundler:config task. Name Purpose Default bundler_config_path Location where the bundler:config task will write bundler\u2019s configuration file \".bundle/config\" bundler_deployment Enables bundler\u2019s deployment mode (strongly recommended) true bundler_gemfile Optionally used to override the location of the Gemfile nil bundler_ignore_messages Hide gem post-install messages during bundle install true bundler_jobs Override bundler\u2019s default (number of processors) amount of concurrency used when downloading/installing gems nil bundler_path Directory where gems where be installed \"%{shared_path}/bundle\" bundler_retry Number of times to retry installing a gem if it fails to download \"3\" bundler_version The version of bundler to install, used by the bundler:upgrade_bundler task; if nil (the default), determine the version based on Gemfile.lock nil bundler_without Array of Gemfile groups to exclude from installation [\"development\", \"test\"] Tasks bundler:upgrade_bundler Installs the version of bundler specified by the :bundler_version setting, if specified. If :bundler_version is nil (the default), this task will automatically determine the version of bundler required by the app that is being deployed by looking at the BUNDLED WITH entry within the app\u2019s Gemfile.lock . Bundler will be installed withing this command: gem install bundler --conservative --no-document -v VERSION bundler:upgrade_bundler is intended for use as a setup task. It should be run prior to bundler:install to ensure that the correct version bundler is present. bundler:config Writes a .bundle/config file with the configuration specified by the various :bundler_* tomo settings. This ensures that invocations of bundle check , bundle install , and most importantly bundle exec all consistently use the correct bundler configuration. bundler:config is intended for use as a setup task. It should be run prior to bundler:install so that gems are installed in the proper location. bundler:install Runs bundle install to download and install all the dependencies specified by the Gemfile of the app that is being deployed. As a performance optimization, this task will run bundle check first to see if the app\u2019s dependencies have already been installed. If so, bundle install is skipped. bundler:install is intended for use as a deploy task. It should be run prior to any tasks that rely on gems. bundler:clean Runs bundle clean to delete any previously installed gems that are no longer needed by the current version of the app. Cleaning is generally good practice to save disk space and speed up app launch time. bundler:clean is intended for use as a deploy task. It should be run at the conclusion of the deploy after all other tasks. Helpers These helper methods become available on instances of Remote when the bundler plugin is loaded. They accept the same options as Remote#run . remote.bundle(*args, **options) \u2192 Tomo::Result Runs bundle within release_path by default. remote.bundle(\"exec\", \"rails\", \"console\") # $ cd /var/www/my-app/current && bundle exec rails console remote.bundle?(*args, **options) \u2192 true or false Same as bundle but returns true if the command succeeded, false otherwise.","title":"bundler"},{"location":"plugins/bundler/#bundler","text":"The bundler plugin installs ruby gem dependencies using bundler. This is required for deploying Rails apps. It also provides conveniences for using bundle exec .","title":"bundler"},{"location":"plugins/bundler/#settings","text":"Note that the settings listed here only take effect if you run the bundler:config task. Name Purpose Default bundler_config_path Location where the bundler:config task will write bundler\u2019s configuration file \".bundle/config\" bundler_deployment Enables bundler\u2019s deployment mode (strongly recommended) true bundler_gemfile Optionally used to override the location of the Gemfile nil bundler_ignore_messages Hide gem post-install messages during bundle install true bundler_jobs Override bundler\u2019s default (number of processors) amount of concurrency used when downloading/installing gems nil bundler_path Directory where gems where be installed \"%{shared_path}/bundle\" bundler_retry Number of times to retry installing a gem if it fails to download \"3\" bundler_version The version of bundler to install, used by the bundler:upgrade_bundler task; if nil (the default), determine the version based on Gemfile.lock nil bundler_without Array of Gemfile groups to exclude from installation [\"development\", \"test\"]","title":"Settings"},{"location":"plugins/bundler/#tasks","text":"","title":"Tasks"},{"location":"plugins/bundler/#bundlerupgrade_bundler","text":"Installs the version of bundler specified by the :bundler_version setting, if specified. If :bundler_version is nil (the default), this task will automatically determine the version of bundler required by the app that is being deployed by looking at the BUNDLED WITH entry within the app\u2019s Gemfile.lock . Bundler will be installed withing this command: gem install bundler --conservative --no-document -v VERSION bundler:upgrade_bundler is intended for use as a setup task. It should be run prior to bundler:install to ensure that the correct version bundler is present.","title":"bundler:upgrade_bundler"},{"location":"plugins/bundler/#bundlerconfig","text":"Writes a .bundle/config file with the configuration specified by the various :bundler_* tomo settings. This ensures that invocations of bundle check , bundle install , and most importantly bundle exec all consistently use the correct bundler configuration. bundler:config is intended for use as a setup task. It should be run prior to bundler:install so that gems are installed in the proper location.","title":"bundler:config"},{"location":"plugins/bundler/#bundlerinstall","text":"Runs bundle install to download and install all the dependencies specified by the Gemfile of the app that is being deployed. As a performance optimization, this task will run bundle check first to see if the app\u2019s dependencies have already been installed. If so, bundle install is skipped. bundler:install is intended for use as a deploy task. It should be run prior to any tasks that rely on gems.","title":"bundler:install"},{"location":"plugins/bundler/#bundlerclean","text":"Runs bundle clean to delete any previously installed gems that are no longer needed by the current version of the app. Cleaning is generally good practice to save disk space and speed up app launch time. bundler:clean is intended for use as a deploy task. It should be run at the conclusion of the deploy after all other tasks.","title":"bundler:clean"},{"location":"plugins/bundler/#helpers","text":"These helper methods become available on instances of Remote when the bundler plugin is loaded. They accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/bundler/#remotebundleargs-options-tomoresult","text":"Runs bundle within release_path by default. remote.bundle(\"exec\", \"rails\", \"console\") # $ cd /var/www/my-app/current && bundle exec rails console","title":"remote.bundle(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/bundler/#remotebundleargs-options-true-or-false","text":"Same as bundle but returns true if the command succeeded, false otherwise.","title":"remote.bundle?(*args, **options) \u2192 true or false"},{"location":"plugins/core/","text":"core The core plugin provides tasks, settings, and helpers that are the fundamental building blocks for most tomo deployments. This plugin is always loaded and available, even if it is not explicitly declared in the configuration file. Settings Name Purpose Default application The name of the application being deployed \"default\" concurrency The maximum number of threads to use when deploying to multiple hosts at once 10 current_path Location of the symlink that points to the currently deployed release \"%{deploy_to}/current\" deploy_to The root directory under which all tomo data, releases, etc. are stored \"/var/www/%{application}\" keep_releases Number of releases to keep when pruning old releases with the core:clean_releases task 10 linked_dirs Array of directory names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] linked_files Array of file names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] local_user User name that will be written to the revision log and the release JSON file as the \u201cdeploy user\u201d. $USER , $USERNAME , or whoami releases_path Directory where the deploy command creates releases \"%{deploy_to}/releases\" release_json_path Path where the core:write_release_json task will write a JSON file describing the release \"%{release_path}/.tomo_release.json\" revision_log_path Path where the core:log_revision task will append a log message with the date and time of the release \"%{deploy_to}/revisions.log\" run_args A special read-only setting where tomo places any extra arguments that are passed to the run command [] shared_path Directory where files shared between releases are stored; used by core:symlink_shared \"%{deploy_to}/shared\" ssh_connect_timeout The number of seconds tomo will wait before it gives up when trying to open an SSH connection 5 ssh_executable The name (or full path) of the ssh executable \"ssh\" ssh_extra_opts An array of extra command line arguments that tomo will pass to every invocation of the ssh executable [\"-o\", \"PasswordAuthentication=no\"] ssh_forward_agent Whether to forward authentication when connecting via SSH; needed for seamless git+ssh true ssh_reuse_connections Whether to use ControlMaster to keep connections open across multiple invocations of ssh; setting this to false will slow down tomo significantly true ssh_strict_host_key_checking Use \"accept-new\" for a good compromise of security and convenience, true for most security, false for most convenience; note that older versions of ssh do not understand the \"accept-new\" option \"accept-new\" tmp_path Directory where the setup command stages temporary files \"/tmp/tomo-#{SecureRandom.alphanumeric(8)}\" tomo_config_file_path A special read-only setting containing the path to the config.rb file that was used to configure tomo \"/path/to/.tomo/config.rb\" Tasks core:setup_directories Creates the :deploy_to , :shared_path , and :releases_path directories so that other tasks that rely on these directories can work. This is one of the first tasks that should be run as part of setup . core:symlink_shared Creates a symlink for each directory listed in the :linked_dirs setting and each file in :linked_files . The symlink will point to the directory or file of the same name inside the shared directory. This allows these directories and files to be shared across all releases. Note that if a directory or file already exists in the release, that directory or file will be deleted or overwritten prior to creating the link. For example, given this configuration: set linked_dirs: [\"public/assets\"] set linked_files: [\"config/database.yml\"] Calling this task will run: mkdir -p /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public cd /var/www/my-app/releases/20190604204415 && rm -rf public/assets ln -sf /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public/assets ln -sfn /var/www/my-app/shared/config/database.yml \\ /var/www/my-app/releases/20190604204415/config/database.yml core:symlink_shared is intended for use as a deploy task. If :linked_dirs and :linked_files are both empty, running this task has no effect. core:symlink_current Promotes the release that is currently being deployed to become the \u201ccurrent\u201d release by updating the current symlink. core:symlink_shared is intended for use as a deploy task. It is typically run after all build steps have completed ( bundler:install , rails:db_migrate , rails:assets_precompile , etc.). core:clean_releases Deletes old releases while maintaining the most recent releases and keeping the current release. The total number of releases kept will be based on the :keep_releases setting. If this setting is absent or zero, running this task has no effect. If you are continuously deploying your application in an automated fashion, the releases can quickly fill up disk space if they are not pruned; hence the need for this task. core:clean_releases is intended for use as a deploy task. It is typically run at the end of a deployment once everything else has succeeded. core:write_release_json Writes a JSON file to the location specified by the :release_json_path setting. This file will contain a JSON object with properties that describe the release. Here is an example: { \"ref\": \"main\", \"author\": \"matt@example.com\", \"revision\": \"0d1cb3212e2f9c43aa49fb172d8d9c726163cecf\", \"revision_date\": \"2019-06-01 17:23:48 -0700\", \"deploy_date\": \"2019-06-05 19:00:26 -0700\", \"deploy_user\": \"mbrictson\" } core:log_revision Appends a message to a log file specified by the :revision_log_path setting. The message contains information about the release. Here is an example entry: 2019-06-05 19:00:26 -0700 - 0d1cb3212e2f9c43aa49fb172d8d9c726163cecf (main) deployed by mbrictson Helpers All of these methods are available on instances of Remote and accept the same options as Remote#run . remote.capture(*command, **options) \u2192 String Run the given command, returning the stdout of that command. If the command did not write to stdout, then return an empty String. Note that stderr is ignored, and an exception will be thrown if the command fails. remote.capture(\"echo\", \"hello\") # => \"hello\\n\" remote.run?(*command, **options) \u2192 true or false Run the given command, returning true if the command succeeded (exit status of 0) or false otherwise. # If java is not installed in the $PATH remote.run?(\"which\", \"java\") # => false remote.write(text:/template:, to:, append: false, **options) \u2192 Tomo::Result Write the given text (a String) or the text resulting from merging the given template (a local path to an ERB template file) to the remote path specified by to: . Refer to the merge_template documentation for details on tomo\u2019s ERB templating behavior. If append is false (the default), the remote file will completely replaced; if true , the file will be appended to. This is designed for small amounts of text (e.g. configuration files), not large or binary data. remote.write text: \"hello world!\\n\", to: paths.shared.join(\"greetings.txt\"), append: true remote.write template: File.expand_path(\"unicorn.service.erb\", __dir__), to: \".config/systemd/user/unicorn.service\" remote.ln_sf(target, link, **options) \u2192 Tomo::Result Create a symlink on the remote host at the path specified at link that points to target . Deletes any existing file that already exists at the link path prior to creating the symlink. remote.ln_sf(paths.shared.join(\".env\"), paths.release.join(\".env\")) # $ ln -sf /var/www/my-app/shared/.env /var/www/my-app/releases/20190604204415/.env remote.ln_sfn(target, link, **options) \u2192 Tomo::Result Like ln_sf but also passes the -n flag, which allows an existing link to be deleted even if it is a symlink to a directory. remote.mkdir_p(*directories, **options) \u2192 Tomo::Result Creates one or more directories on the remote host. remote.mkdir_p(paths.current.dirname, paths.shared) # $ mkdir -p /var/www/my-app /var/www/my-app/shared remote.rm_rf(*paths, **options) \u2192 Tomo::Result Deletes one or more files or directories on the remote host. remote.rm_rf(paths.tmp) # $ rm -rf /tmp/tomo-a4DBHX0P remote.list_files(directory=nil, **options) \u2192 [String] Lists non-hidden files in the specified directory. If directory is omitted, the default SSH login directory is used (typically the deploy user\u2019s home directory). The result will be an array of the directory contents. remote.list_files(\"/var/www/my-app\") # => [\"current\", \"releases\", \"revision.log\", shared\"] remote.command_available?(command_name, **options) \u2192 true or false Runs which on the remote host to determine whether the given command_name is an available executable. Returns true if an executable exists, false otherwise. remote.command_available?(\"java\") # => false remote.file?(file, **options) \u2192 true or false Uses the shell expression [ -f ] to test whether the given file exists on the remote host. Returns true if the path exists and is a normal file, false otherwise. remote.file?(\".bashrc\") # => true remote.executable?(file, **options) \u2192 true or false Uses the shell expression [ -x ] to test whether the given file exists on the remote host. Returns true if the path exists and is executable, false otherwise. remote.executable?(\"/usr/bin/git\") # => true remote.directory?(directory, **options) \u2192 true or false Uses the shell expression [ -d ] to test whether the given directory exists on the remote host. Returns true if the path exists and is a directory, false otherwise. remote.directory?(\"/opt\") # => true","title":"core"},{"location":"plugins/core/#core","text":"The core plugin provides tasks, settings, and helpers that are the fundamental building blocks for most tomo deployments. This plugin is always loaded and available, even if it is not explicitly declared in the configuration file.","title":"core"},{"location":"plugins/core/#settings","text":"Name Purpose Default application The name of the application being deployed \"default\" concurrency The maximum number of threads to use when deploying to multiple hosts at once 10 current_path Location of the symlink that points to the currently deployed release \"%{deploy_to}/current\" deploy_to The root directory under which all tomo data, releases, etc. are stored \"/var/www/%{application}\" keep_releases Number of releases to keep when pruning old releases with the core:clean_releases task 10 linked_dirs Array of directory names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] linked_files Array of file names that will be symlinked from the shared directory into each release by the core:symlink_shared task [] local_user User name that will be written to the revision log and the release JSON file as the \u201cdeploy user\u201d. $USER , $USERNAME , or whoami releases_path Directory where the deploy command creates releases \"%{deploy_to}/releases\" release_json_path Path where the core:write_release_json task will write a JSON file describing the release \"%{release_path}/.tomo_release.json\" revision_log_path Path where the core:log_revision task will append a log message with the date and time of the release \"%{deploy_to}/revisions.log\" run_args A special read-only setting where tomo places any extra arguments that are passed to the run command [] shared_path Directory where files shared between releases are stored; used by core:symlink_shared \"%{deploy_to}/shared\" ssh_connect_timeout The number of seconds tomo will wait before it gives up when trying to open an SSH connection 5 ssh_executable The name (or full path) of the ssh executable \"ssh\" ssh_extra_opts An array of extra command line arguments that tomo will pass to every invocation of the ssh executable [\"-o\", \"PasswordAuthentication=no\"] ssh_forward_agent Whether to forward authentication when connecting via SSH; needed for seamless git+ssh true ssh_reuse_connections Whether to use ControlMaster to keep connections open across multiple invocations of ssh; setting this to false will slow down tomo significantly true ssh_strict_host_key_checking Use \"accept-new\" for a good compromise of security and convenience, true for most security, false for most convenience; note that older versions of ssh do not understand the \"accept-new\" option \"accept-new\" tmp_path Directory where the setup command stages temporary files \"/tmp/tomo-#{SecureRandom.alphanumeric(8)}\" tomo_config_file_path A special read-only setting containing the path to the config.rb file that was used to configure tomo \"/path/to/.tomo/config.rb\"","title":"Settings"},{"location":"plugins/core/#tasks","text":"","title":"Tasks"},{"location":"plugins/core/#coresetup_directories","text":"Creates the :deploy_to , :shared_path , and :releases_path directories so that other tasks that rely on these directories can work. This is one of the first tasks that should be run as part of setup .","title":"core:setup_directories"},{"location":"plugins/core/#coresymlink_shared","text":"Creates a symlink for each directory listed in the :linked_dirs setting and each file in :linked_files . The symlink will point to the directory or file of the same name inside the shared directory. This allows these directories and files to be shared across all releases. Note that if a directory or file already exists in the release, that directory or file will be deleted or overwritten prior to creating the link. For example, given this configuration: set linked_dirs: [\"public/assets\"] set linked_files: [\"config/database.yml\"] Calling this task will run: mkdir -p /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public cd /var/www/my-app/releases/20190604204415 && rm -rf public/assets ln -sf /var/www/my-app/shared/public/assets \\ /var/www/my-app/releases/20190604204415/public/assets ln -sfn /var/www/my-app/shared/config/database.yml \\ /var/www/my-app/releases/20190604204415/config/database.yml core:symlink_shared is intended for use as a deploy task. If :linked_dirs and :linked_files are both empty, running this task has no effect.","title":"core:symlink_shared"},{"location":"plugins/core/#coresymlink_current","text":"Promotes the release that is currently being deployed to become the \u201ccurrent\u201d release by updating the current symlink. core:symlink_shared is intended for use as a deploy task. It is typically run after all build steps have completed ( bundler:install , rails:db_migrate , rails:assets_precompile , etc.).","title":"core:symlink_current"},{"location":"plugins/core/#coreclean_releases","text":"Deletes old releases while maintaining the most recent releases and keeping the current release. The total number of releases kept will be based on the :keep_releases setting. If this setting is absent or zero, running this task has no effect. If you are continuously deploying your application in an automated fashion, the releases can quickly fill up disk space if they are not pruned; hence the need for this task. core:clean_releases is intended for use as a deploy task. It is typically run at the end of a deployment once everything else has succeeded.","title":"core:clean_releases"},{"location":"plugins/core/#corewrite_release_json","text":"Writes a JSON file to the location specified by the :release_json_path setting. This file will contain a JSON object with properties that describe the release. Here is an example: { \"ref\": \"main\", \"author\": \"matt@example.com\", \"revision\": \"0d1cb3212e2f9c43aa49fb172d8d9c726163cecf\", \"revision_date\": \"2019-06-01 17:23:48 -0700\", \"deploy_date\": \"2019-06-05 19:00:26 -0700\", \"deploy_user\": \"mbrictson\" }","title":"core:write_release_json"},{"location":"plugins/core/#corelog_revision","text":"Appends a message to a log file specified by the :revision_log_path setting. The message contains information about the release. Here is an example entry: 2019-06-05 19:00:26 -0700 - 0d1cb3212e2f9c43aa49fb172d8d9c726163cecf (main) deployed by mbrictson","title":"core:log_revision"},{"location":"plugins/core/#helpers","text":"All of these methods are available on instances of Remote and accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/core/#remotecapturecommand-options-string","text":"Run the given command, returning the stdout of that command. If the command did not write to stdout, then return an empty String. Note that stderr is ignored, and an exception will be thrown if the command fails. remote.capture(\"echo\", \"hello\") # => \"hello\\n\"","title":"remote.capture(*command, **options) \u2192 String"},{"location":"plugins/core/#remoteruncommand-options-true-or-false","text":"Run the given command, returning true if the command succeeded (exit status of 0) or false otherwise. # If java is not installed in the $PATH remote.run?(\"which\", \"java\") # => false","title":"remote.run?(*command, **options) \u2192 true or false"},{"location":"plugins/core/#remotewritetexttemplate-to-append-false-options-tomoresult","text":"Write the given text (a String) or the text resulting from merging the given template (a local path to an ERB template file) to the remote path specified by to: . Refer to the merge_template documentation for details on tomo\u2019s ERB templating behavior. If append is false (the default), the remote file will completely replaced; if true , the file will be appended to. This is designed for small amounts of text (e.g. configuration files), not large or binary data. remote.write text: \"hello world!\\n\", to: paths.shared.join(\"greetings.txt\"), append: true remote.write template: File.expand_path(\"unicorn.service.erb\", __dir__), to: \".config/systemd/user/unicorn.service\"","title":"remote.write(text:/template:, to:, append: false, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remoteln_sftarget-link-options-tomoresult","text":"Create a symlink on the remote host at the path specified at link that points to target . Deletes any existing file that already exists at the link path prior to creating the symlink. remote.ln_sf(paths.shared.join(\".env\"), paths.release.join(\".env\")) # $ ln -sf /var/www/my-app/shared/.env /var/www/my-app/releases/20190604204415/.env","title":"remote.ln_sf(target, link, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remoteln_sfntarget-link-options-tomoresult","text":"Like ln_sf but also passes the -n flag, which allows an existing link to be deleted even if it is a symlink to a directory.","title":"remote.ln_sfn(target, link, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remotemkdir_pdirectories-options-tomoresult","text":"Creates one or more directories on the remote host. remote.mkdir_p(paths.current.dirname, paths.shared) # $ mkdir -p /var/www/my-app /var/www/my-app/shared","title":"remote.mkdir_p(*directories, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remoterm_rfpaths-options-tomoresult","text":"Deletes one or more files or directories on the remote host. remote.rm_rf(paths.tmp) # $ rm -rf /tmp/tomo-a4DBHX0P","title":"remote.rm_rf(*paths, **options) \u2192 Tomo::Result"},{"location":"plugins/core/#remotelist_filesdirectorynil-options-string","text":"Lists non-hidden files in the specified directory. If directory is omitted, the default SSH login directory is used (typically the deploy user\u2019s home directory). The result will be an array of the directory contents. remote.list_files(\"/var/www/my-app\") # => [\"current\", \"releases\", \"revision.log\", shared\"]","title":"remote.list_files(directory=nil, **options) \u2192 [String]"},{"location":"plugins/core/#remotecommand_availablecommand_name-options-true-or-false","text":"Runs which on the remote host to determine whether the given command_name is an available executable. Returns true if an executable exists, false otherwise. remote.command_available?(\"java\") # => false","title":"remote.command_available?(command_name, **options) \u2192 true or false"},{"location":"plugins/core/#remotefilefile-options-true-or-false","text":"Uses the shell expression [ -f ] to test whether the given file exists on the remote host. Returns true if the path exists and is a normal file, false otherwise. remote.file?(\".bashrc\") # => true","title":"remote.file?(file, **options) \u2192 true or false"},{"location":"plugins/core/#remoteexecutablefile-options-true-or-false","text":"Uses the shell expression [ -x ] to test whether the given file exists on the remote host. Returns true if the path exists and is executable, false otherwise. remote.executable?(\"/usr/bin/git\") # => true","title":"remote.executable?(file, **options) \u2192 true or false"},{"location":"plugins/core/#remotedirectorydirectory-options-true-or-false","text":"Uses the shell expression [ -d ] to test whether the given directory exists on the remote host. Returns true if the path exists and is a directory, false otherwise. remote.directory?(\"/opt\") # => true","title":"remote.directory?(directory, **options) \u2192 true or false"},{"location":"plugins/env/","text":"env The env plugin manages environment variables on the remote host. It does this by creating an envrc file on the remote host and modifying the .bashrc of the deploy user so that the envrc is always loaded (for both interactive and non-interactive SSH sessions). There are two ways to specify the environment variables that are stored in the envrc file: Use env:set via the command line like tomo run env:set NAME[=VALUE] ... to explicitly set or modify environment variables Specify the :env_vars setting in the tomo configuration and then run the env:update task Note that in order for these tasks to work, you must first run env:setup to ensure the deploy user\u2019s .bashrc is properly configured to read from the envrc file that is managed by this plugin. Settings Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" env_path Location of the envrc file on the remote host \"%{deploy_to}/envrc\" env_vars A hash of environment variable names and values that will configured on the remote host; see env:update for details {} Tasks env:setup Performs an env:update and then modifies the deploy user\u2019s bashrc so that the envrc is automatically loaded for all future SSH sessions. Specifically, this is what is added to the top of the .bashrc file: if [ -f /var/www/my-app/envrc ]; then . /var/www/my-app/envrc fi env:setup is intended for use as a setup task. It must be run before other env tasks. env:update Ensures that all environment variables that are specified in the :env_vars setting are present in the envrc file on the remote host, modifying the envrc file if necessary. For example, given this config: set env_vars: { RAILS_ENV: \"production\", PUMA_THREADS: 20 } This task will ensure that the envrc file is updated to include: export RAILS_ENV=production export PUMA_THREADS=20 :prompt For environment variables that are used for API keys or other sensitive data, you can specify :prompt instead of the actual value. In this case tomo will prompt interactively for the value the first time it is needed. For example: set env_vars: { DATABASE_URL: :prompt } The first time env:update is run, tomo will prompt for the value: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to user@app.example.com \u2022 env:update DATABASE_URL? Once the environment variable exists in the envrc file, tomo will no longer prompt for it. :generate_secret Similarly, for environment variables that requires a randomly generated secret value, like SECRET_KEY_BASE , you can specify :generate_secret . In this case, tomo will generate a value using SecureRandom.hex(64) the first time it is needed. set env_vars: { SECRET_KEY_BASE: :generate_secret } env:update is intended for use as a deploy task. It should be run at the beginning of a deploy to ensure that the environment has all the latest values before other tasks are run. env:set Set one or more environment variables in the remote envrc file. This task is intended for use with run and takes command-line arguments. There are two forms: # Set the remote envrc var named KEY to have VALUE $ tomo run env:set KEY=VALUE # Prompt interactively for the value of KEY and then set it in the remote envrc $ tomo run env:set KEY KEY? env:unset Remove one or more environment variables from the remote envrc file. This task is intended for use with run and takes command-line arguments. # Remove the remote envrc var named KEY $ tomo run env:unset KEY env:show Display the contents of the remote envrc file. This task is intended for use with run . $ tomo run env:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 env:show RAILS_ENV=production SECRET_KEY_BASE=02d587d76e80b2266289adef13fc045dd8387ede92935bcc1d49aa89932e5f74c35ed25bbdc41d3cf6cfc7f5f7f1736199997be459251aec52e42797c5140743 \u2714 Ran env:show on deployer@app.example.com","title":"env"},{"location":"plugins/env/#env","text":"The env plugin manages environment variables on the remote host. It does this by creating an envrc file on the remote host and modifying the .bashrc of the deploy user so that the envrc is always loaded (for both interactive and non-interactive SSH sessions). There are two ways to specify the environment variables that are stored in the envrc file: Use env:set via the command line like tomo run env:set NAME[=VALUE] ... to explicitly set or modify environment variables Specify the :env_vars setting in the tomo configuration and then run the env:update task Note that in order for these tasks to work, you must first run env:setup to ensure the deploy user\u2019s .bashrc is properly configured to read from the envrc file that is managed by this plugin.","title":"env"},{"location":"plugins/env/#settings","text":"Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" env_path Location of the envrc file on the remote host \"%{deploy_to}/envrc\" env_vars A hash of environment variable names and values that will configured on the remote host; see env:update for details {}","title":"Settings"},{"location":"plugins/env/#tasks","text":"","title":"Tasks"},{"location":"plugins/env/#envsetup","text":"Performs an env:update and then modifies the deploy user\u2019s bashrc so that the envrc is automatically loaded for all future SSH sessions. Specifically, this is what is added to the top of the .bashrc file: if [ -f /var/www/my-app/envrc ]; then . /var/www/my-app/envrc fi env:setup is intended for use as a setup task. It must be run before other env tasks.","title":"env:setup"},{"location":"plugins/env/#envupdate","text":"Ensures that all environment variables that are specified in the :env_vars setting are present in the envrc file on the remote host, modifying the envrc file if necessary. For example, given this config: set env_vars: { RAILS_ENV: \"production\", PUMA_THREADS: 20 } This task will ensure that the envrc file is updated to include: export RAILS_ENV=production export PUMA_THREADS=20","title":"env:update"},{"location":"plugins/env/#prompt","text":"For environment variables that are used for API keys or other sensitive data, you can specify :prompt instead of the actual value. In this case tomo will prompt interactively for the value the first time it is needed. For example: set env_vars: { DATABASE_URL: :prompt } The first time env:update is run, tomo will prompt for the value: $ tomo deploy tomo deploy v1.0.0 \u2192 Connecting to user@app.example.com \u2022 env:update DATABASE_URL? Once the environment variable exists in the envrc file, tomo will no longer prompt for it.","title":":prompt"},{"location":"plugins/env/#generate_secret","text":"Similarly, for environment variables that requires a randomly generated secret value, like SECRET_KEY_BASE , you can specify :generate_secret . In this case, tomo will generate a value using SecureRandom.hex(64) the first time it is needed. set env_vars: { SECRET_KEY_BASE: :generate_secret } env:update is intended for use as a deploy task. It should be run at the beginning of a deploy to ensure that the environment has all the latest values before other tasks are run.","title":":generate_secret"},{"location":"plugins/env/#envset","text":"Set one or more environment variables in the remote envrc file. This task is intended for use with run and takes command-line arguments. There are two forms: # Set the remote envrc var named KEY to have VALUE $ tomo run env:set KEY=VALUE # Prompt interactively for the value of KEY and then set it in the remote envrc $ tomo run env:set KEY KEY?","title":"env:set"},{"location":"plugins/env/#envunset","text":"Remove one or more environment variables from the remote envrc file. This task is intended for use with run and takes command-line arguments. # Remove the remote envrc var named KEY $ tomo run env:unset KEY","title":"env:unset"},{"location":"plugins/env/#envshow","text":"Display the contents of the remote envrc file. This task is intended for use with run . $ tomo run env:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 env:show RAILS_ENV=production SECRET_KEY_BASE=02d587d76e80b2266289adef13fc045dd8387ede92935bcc1d49aa89932e5f74c35ed25bbdc41d3cf6cfc7f5f7f1736199997be459251aec52e42797c5140743 \u2714 Ran env:show on deployer@app.example.com","title":"env:show"},{"location":"plugins/git/","text":"git The git plugin uses git running on the remote host to fetch the code of the app being deployed. This \u201cremote pull\u201d technique is currently the only deployment method officially supported by tomo. For this to work, the SSH key you use to connect to the remote host via tomo must match the key expected by the git host (e.g. by GitHub). Settings Name Purpose Default git_branch The branch of the repository to deploy nil git_repo_path Directory on the remote host where a cache of the repository will be stored \"%{deploy_to}/git_repo\" git_exclusions An array of paths (similar to gitignore syntax) that will be excluded when the repository is copied into a release; it is recommend you exclude .tomo/ and other directories not needed in production, like spec/ [] git_env Environment variables that will be set when issuing git commands (hash) { GIT_SSH_COMMAND: \"ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no\" } git_ref The commit SHA or tag to deploy (overrides :git_branch ) nil git_url URL of the git repository; always use the SSH form like git@github.com:username/repo.git (not HTTPS) nil git_user_name The value to use for git\u2019s user.name config nil git_user_email The value to use for git\u2019s user.email config nil Tasks git:config Globally (for the deployer user) configures git with values for user.name and user.email so that certain git commands are able to function. By default, tomo will use the deployer username and append @example.com to generate these values, like this: git config --global user.name deployer git config --global user.email deployer@example.com If you wish to customize the name and email values, use the git_user_name and git_user_email settings. git:config is intended for use as a setup task. git:clone Performs the initial clone of the git repository. This is necessary before a deploy can be performed. The clone of the repository will be stored in the git_repo_path . The git_url setting must be specified for this task to work. git:clone is intended for use as a setup task. git:create_release Fetches the latest commits from git_branch (or git_ref ) and creates a release by copying the contents of that branch of repository into a new release inside the releases_path . Releases are numbered based on the timestamp of when the deploy takes place. git:create_release is intended for use as a deploy task. Helpers These helper methods become available on instances of Remote when the git plugin is loaded. They accept the same options as Remote#run . remote.git(*args, **options) \u2192 Tomo::Result Runs git with the environment variables specified by the git_env setting. remote.git(\"fetch\") # $ export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication=no\\ -o\\ StrictHostKeyChecking=no && git fetch","title":"git"},{"location":"plugins/git/#git","text":"The git plugin uses git running on the remote host to fetch the code of the app being deployed. This \u201cremote pull\u201d technique is currently the only deployment method officially supported by tomo. For this to work, the SSH key you use to connect to the remote host via tomo must match the key expected by the git host (e.g. by GitHub).","title":"git"},{"location":"plugins/git/#settings","text":"Name Purpose Default git_branch The branch of the repository to deploy nil git_repo_path Directory on the remote host where a cache of the repository will be stored \"%{deploy_to}/git_repo\" git_exclusions An array of paths (similar to gitignore syntax) that will be excluded when the repository is copied into a release; it is recommend you exclude .tomo/ and other directories not needed in production, like spec/ [] git_env Environment variables that will be set when issuing git commands (hash) { GIT_SSH_COMMAND: \"ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no\" } git_ref The commit SHA or tag to deploy (overrides :git_branch ) nil git_url URL of the git repository; always use the SSH form like git@github.com:username/repo.git (not HTTPS) nil git_user_name The value to use for git\u2019s user.name config nil git_user_email The value to use for git\u2019s user.email config nil","title":"Settings"},{"location":"plugins/git/#tasks","text":"","title":"Tasks"},{"location":"plugins/git/#gitconfig","text":"Globally (for the deployer user) configures git with values for user.name and user.email so that certain git commands are able to function. By default, tomo will use the deployer username and append @example.com to generate these values, like this: git config --global user.name deployer git config --global user.email deployer@example.com If you wish to customize the name and email values, use the git_user_name and git_user_email settings. git:config is intended for use as a setup task.","title":"git:config"},{"location":"plugins/git/#gitclone","text":"Performs the initial clone of the git repository. This is necessary before a deploy can be performed. The clone of the repository will be stored in the git_repo_path . The git_url setting must be specified for this task to work. git:clone is intended for use as a setup task.","title":"git:clone"},{"location":"plugins/git/#gitcreate_release","text":"Fetches the latest commits from git_branch (or git_ref ) and creates a release by copying the contents of that branch of repository into a new release inside the releases_path . Releases are numbered based on the timestamp of when the deploy takes place. git:create_release is intended for use as a deploy task.","title":"git:create_release"},{"location":"plugins/git/#helpers","text":"These helper methods become available on instances of Remote when the git plugin is loaded. They accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/git/#remotegitargs-options-tomoresult","text":"Runs git with the environment variables specified by the git_env setting. remote.git(\"fetch\") # $ export GIT_SSH_COMMAND=ssh\\ -o\\ PasswordAuthentication=no\\ -o\\ StrictHostKeyChecking=no && git fetch","title":"remote.git(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/nodenv/","text":"nodenv The nodenv plugin installs node and yarn. This allows you to deploy an app with confidence that yarn and a particular version of node will be available on the host. This plugin is strongly recommended for Rails apps, which by default use webpacker and thus require node and yarn. Settings Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" nodenv_install_yarn Whether to install yarn globally via npm i -g yarn true nodenv_node_version Version of node to install; if nil (the default), determine the version based on .node-version nil nodenv_yarn_version A value of nil (the default) means install the latest; specify this only if you need a specific 1.y.z global version of yarn nil Tasks nodenv:install Installs nodenv, uses nodenv to install node, and makes the desired version of node the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that nodenv is automatically loaded for interactive and non-interactive shells. You must supply a value for the nodenv_node_version setting or have a .node-version file in your project for this task to work. By default, yarn is also installed globally via npm. This can be disabled by setting nodenv_install_yarn to false . nodenv:install is intended for use as a setup task.","title":"nodenv"},{"location":"plugins/nodenv/#nodenv","text":"The nodenv plugin installs node and yarn. This allows you to deploy an app with confidence that yarn and a particular version of node will be available on the host. This plugin is strongly recommended for Rails apps, which by default use webpacker and thus require node and yarn.","title":"nodenv"},{"location":"plugins/nodenv/#settings","text":"Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" nodenv_install_yarn Whether to install yarn globally via npm i -g yarn true nodenv_node_version Version of node to install; if nil (the default), determine the version based on .node-version nil nodenv_yarn_version A value of nil (the default) means install the latest; specify this only if you need a specific 1.y.z global version of yarn nil","title":"Settings"},{"location":"plugins/nodenv/#tasks","text":"","title":"Tasks"},{"location":"plugins/nodenv/#nodenvinstall","text":"Installs nodenv, uses nodenv to install node, and makes the desired version of node the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that nodenv is automatically loaded for interactive and non-interactive shells. You must supply a value for the nodenv_node_version setting or have a .node-version file in your project for this task to work. By default, yarn is also installed globally via npm. This can be disabled by setting nodenv_install_yarn to false . nodenv:install is intended for use as a setup task.","title":"nodenv:install"},{"location":"plugins/puma/","text":"puma The puma plugin provides a systemd -based solution for starting, stopping, and restarting puma using socket activation for zero-downtime restarts. It is based on the best practices in the official puma documentation . Tomo\u2019s implementation installs puma as a user-level service using systemctl --user . This allows puma to be installed, started, stopped, and restarted without a root user or sudo . However, when provisioning the host you must make sure to run the following command as root to allow the puma process to continue running even after the tomo deploy user disconnects: # run as root $ loginctl enable-linger Stdout and stderr of the puma process will be routed to syslog, as is the convention for systemd services. For Rails, it is recommended that you set RAILS_LOG_TO_STDOUT=1 so that all Rails logs are handled this way ( tomo init configures this by default). The tomo puma plugin assumes that your puma server will listen on a single TCP port for HTTP (not HTTPS) traffic. In other words, HTTPS termination will be handled by e.g. Nginx or a separate load balancer. Settings Name Purpose Default puma_check_timeout The number of seconds that the puma:check_active task will wait for puma to respond before timing out. 15 puma_host Hostname / IP address that puma should listen on 0.0.0.0 (set to 127.0.0.1 to accept only internal connections) puma_port TCP port that puma should listen on 3000 puma_systemd_service Name of the systemd service that manages the puma server \"puma_%{application}.service\" puma_systemd_socket Name of the systemd socket that is used for socket activation of the puma service \"puma_%{application}.socket\" puma_systemd_service_path Path on the remote host where the systemd puma service configuration file will be created \".config/systemd/user/%{puma_systemd_service}\" puma_systemd_service_type If set to \"notify\" , Puma will automatically be restarted if it locks up; change to \"simple\" if using JRuby or Puma < 5.1 \"notify\" puma_systemd_socket_path Path on the remote host where the systemd puma socket configuration file will be created \".config/systemd/user/%{puma_systemd_socket}\" puma_systemd_service_template_path Local path of the ERB template to use to create the the systemd puma service configuration file service.erb puma_systemd_socket_template_path Local path of the ERB template to use to create the the systemd puma socket configuration file socket.erb Tasks puma:setup_systemd Configures systemd to manage puma. This means that puma will automatically be restarted if it crashes, or if the host is rebooted. This task essentially does three things: Installs a puma.socket systemd unit Installs a puma.service systemd unit that depends on the socket Enables these units using systemctl --user enable Note that these units will be installed and run for the deploy user. You can use :puma_systemd_socket_template_path and :puma_systemd_service_template_path to provide your own templates and customize how puma and systemd are configured. puma:setup_systemd is intended for use as a setup task. It must be run before puma can be started during a deploy. puma:restart Restarts the puma service via systemd. This starts puma if it isn\u2019t running already. The systemd socket remains running while puma itself is restarted. In other words, incoming requests will continue to connect and queue while puma restarts. This is a \u201czero-downtime restart\u201d. Puma will be configured to listen on :puma_port , with the config/puma.rb file within the Rails app providing the remainder of the configuration. The default port is 3000. Puma is started using this command: bundle exec --keep-file-descriptors puma -C config/puma.rb -b tcp://0.0.0.0:3000 puma:restart is intended for use in a deploy , immediately following core:symlink_current to ensure that the new version of the Rails app is activated. puma:check_active This task queries systemd and executes a curl test to verify that puma is active and listening on :puma_port . Because puma is run in the background, it is not immediately obvious after starting or restarting puma via systemd as to whether it booted successfully, or if it crashed. This is where the puma:check_active task can help. If puma is not working it will fail and show puma\u2019s log output for easier troubleshooting. puma:check_active is intended for use as a deploy task, immediately following puma:restart to verify that puma restarted successfully. puma:start Starts the puma socket and service via systemd, if they aren\u2019t running already. Equivalent to: systemctl --user start puma.socket puma.service puma:stop Stops the puma socket and service via systemd. Equivalent to: systemctl --user stop puma.socket puma.service puma:status Reports the status of the puma socket and service via systemd. Equivalent to: systemctl --user status puma.socket puma.service Sample output: $ tomo run puma:status tomo run v0.10.0 \u2192 Connecting to deployer@app.example.com \u2022 puma:status systemctl --user status puma_example.socket puma_example.service \u25cf puma_example.socket - Puma HTTP Server Accept Sockets for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.socket; enabled; vendor preset: enabled) Active: active (running) since Thu 2019-10-24 09:41:53 UTC; 1 weeks 2 days ago Listen: 0.0.0.0:3000 (Stream) \u25cf puma_example.service - Puma HTTP Server for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2019-11-01 15:46:10 UTC; 1 day 10h ago Main PID: 14513 (bundle) CGroup: /user.slice/user-1000.slice/user@1000.service/puma_example.service \u2514\u250014513 puma 4.2.1 (tcp://0.0.0.0:3000) [20191101154450] puma:log Uses journalctl (part of systemd) to view the log output of the puma service. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the journalctl command. For example: $ tomo run -- puma:log -f Will run this remote script: journalctl -q --user-unit=puma.service -f puma:tail_log A convenience method for tailing the puma logs. Equivalent to tomo run -- puma:log -f","title":"puma"},{"location":"plugins/puma/#puma","text":"The puma plugin provides a systemd -based solution for starting, stopping, and restarting puma using socket activation for zero-downtime restarts. It is based on the best practices in the official puma documentation . Tomo\u2019s implementation installs puma as a user-level service using systemctl --user . This allows puma to be installed, started, stopped, and restarted without a root user or sudo . However, when provisioning the host you must make sure to run the following command as root to allow the puma process to continue running even after the tomo deploy user disconnects: # run as root $ loginctl enable-linger Stdout and stderr of the puma process will be routed to syslog, as is the convention for systemd services. For Rails, it is recommended that you set RAILS_LOG_TO_STDOUT=1 so that all Rails logs are handled this way ( tomo init configures this by default). The tomo puma plugin assumes that your puma server will listen on a single TCP port for HTTP (not HTTPS) traffic. In other words, HTTPS termination will be handled by e.g. Nginx or a separate load balancer.","title":"puma"},{"location":"plugins/puma/#settings","text":"Name Purpose Default puma_check_timeout The number of seconds that the puma:check_active task will wait for puma to respond before timing out. 15 puma_host Hostname / IP address that puma should listen on 0.0.0.0 (set to 127.0.0.1 to accept only internal connections) puma_port TCP port that puma should listen on 3000 puma_systemd_service Name of the systemd service that manages the puma server \"puma_%{application}.service\" puma_systemd_socket Name of the systemd socket that is used for socket activation of the puma service \"puma_%{application}.socket\" puma_systemd_service_path Path on the remote host where the systemd puma service configuration file will be created \".config/systemd/user/%{puma_systemd_service}\" puma_systemd_service_type If set to \"notify\" , Puma will automatically be restarted if it locks up; change to \"simple\" if using JRuby or Puma < 5.1 \"notify\" puma_systemd_socket_path Path on the remote host where the systemd puma socket configuration file will be created \".config/systemd/user/%{puma_systemd_socket}\" puma_systemd_service_template_path Local path of the ERB template to use to create the the systemd puma service configuration file service.erb puma_systemd_socket_template_path Local path of the ERB template to use to create the the systemd puma socket configuration file socket.erb","title":"Settings"},{"location":"plugins/puma/#tasks","text":"","title":"Tasks"},{"location":"plugins/puma/#pumasetup_systemd","text":"Configures systemd to manage puma. This means that puma will automatically be restarted if it crashes, or if the host is rebooted. This task essentially does three things: Installs a puma.socket systemd unit Installs a puma.service systemd unit that depends on the socket Enables these units using systemctl --user enable Note that these units will be installed and run for the deploy user. You can use :puma_systemd_socket_template_path and :puma_systemd_service_template_path to provide your own templates and customize how puma and systemd are configured. puma:setup_systemd is intended for use as a setup task. It must be run before puma can be started during a deploy.","title":"puma:setup_systemd"},{"location":"plugins/puma/#pumarestart","text":"Restarts the puma service via systemd. This starts puma if it isn\u2019t running already. The systemd socket remains running while puma itself is restarted. In other words, incoming requests will continue to connect and queue while puma restarts. This is a \u201czero-downtime restart\u201d. Puma will be configured to listen on :puma_port , with the config/puma.rb file within the Rails app providing the remainder of the configuration. The default port is 3000. Puma is started using this command: bundle exec --keep-file-descriptors puma -C config/puma.rb -b tcp://0.0.0.0:3000 puma:restart is intended for use in a deploy , immediately following core:symlink_current to ensure that the new version of the Rails app is activated.","title":"puma:restart"},{"location":"plugins/puma/#pumacheck_active","text":"This task queries systemd and executes a curl test to verify that puma is active and listening on :puma_port . Because puma is run in the background, it is not immediately obvious after starting or restarting puma via systemd as to whether it booted successfully, or if it crashed. This is where the puma:check_active task can help. If puma is not working it will fail and show puma\u2019s log output for easier troubleshooting. puma:check_active is intended for use as a deploy task, immediately following puma:restart to verify that puma restarted successfully.","title":"puma:check_active"},{"location":"plugins/puma/#pumastart","text":"Starts the puma socket and service via systemd, if they aren\u2019t running already. Equivalent to: systemctl --user start puma.socket puma.service","title":"puma:start"},{"location":"plugins/puma/#pumastop","text":"Stops the puma socket and service via systemd. Equivalent to: systemctl --user stop puma.socket puma.service","title":"puma:stop"},{"location":"plugins/puma/#pumastatus","text":"Reports the status of the puma socket and service via systemd. Equivalent to: systemctl --user status puma.socket puma.service Sample output: $ tomo run puma:status tomo run v0.10.0 \u2192 Connecting to deployer@app.example.com \u2022 puma:status systemctl --user status puma_example.socket puma_example.service \u25cf puma_example.socket - Puma HTTP Server Accept Sockets for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.socket; enabled; vendor preset: enabled) Active: active (running) since Thu 2019-10-24 09:41:53 UTC; 1 weeks 2 days ago Listen: 0.0.0.0:3000 (Stream) \u25cf puma_example.service - Puma HTTP Server for example Loaded: loaded (/home/deployer/.config/systemd/user/puma_example.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2019-11-01 15:46:10 UTC; 1 day 10h ago Main PID: 14513 (bundle) CGroup: /user.slice/user-1000.slice/user@1000.service/puma_example.service \u2514\u250014513 puma 4.2.1 (tcp://0.0.0.0:3000) [20191101154450]","title":"puma:status"},{"location":"plugins/puma/#pumalog","text":"Uses journalctl (part of systemd) to view the log output of the puma service. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the journalctl command. For example: $ tomo run -- puma:log -f Will run this remote script: journalctl -q --user-unit=puma.service -f","title":"puma:log"},{"location":"plugins/puma/#pumatail_log","text":"A convenience method for tailing the puma logs. Equivalent to tomo run -- puma:log -f","title":"puma:tail_log"},{"location":"plugins/rails/","text":"rails The rails plugin provides tasks for running rails and rake commands commonly used during setup and deployment, such as for precompiling assets and migrating the database. Make sure the RAILS_ENV environment variable is set prior to running rails tasks. The env plugin is the preferred mechanism for this. Settings None. Tasks rails:assets_precompile Builds the asset pipeline in preparation for deployment. This is necessary for Rails apps that use the asset pipeline, which is all new Rails apps by default. Running this task will execute this script: cd /var/www/my-app/releases/ && bundle exec rake assets:precompile rails:assets_precompile is intended for use as a deploy task. It is typically run just prior to core:symlink_current to activate a new release. rails:console Starts an interactive Rails console via SSH to the remote host. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the console. For example: $ tomo run -- rails:console --sandbox Will run this remote script: cd /var/www/my-app/current && bundle exec rails console --sandbox rails:db_console Starts an interactive database console (e.g. psql) for the primary Rails database via SSH to the remote host. This task is intended for use as a run task. The include-password option is passed automatically. $ tomo run rails:db_console Will run this remote script: cd /var/www/my-app/current && bundle exec rails dbconsole --include-password rails:db_migrate Migrates the database by running: cd /var/www/my-app/releases/ && bundle exec rake db:migrate rails:db_migrate is intended for use as a deploy task. It is typically run just after bundler:install prior to activating a new release. rails:db_seed Loads seed data into the database. Seeds should be written to be idempotent, such that it is safe to seed the database on each deploy. Typically seeds are used to load reference data need for the app to function, or for example to create an initial admin user. This task runs the following script: cd /var/www/my-app/releases/ && bundle exec rake db:seed rails:db_seed is intended for use as a deploy task. Since seeds rely on the structure of the database, it is typically run just after rails:db_migrate . rails:db_create Runs bundle exec rake db:create to create the database. This task is intended for use as a setup task. It will be automatically skipped if the database already exists, so it is safe to re-run. rails:db_schema_load Runs bundle exec rake db:schema:load to load the schema from db/schema.rb into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run. rails:db_structure_load Runs bundle exec rake db:structure:load to load the schema from db/structure.sql into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run. Helpers These helper methods become available on instances of Remote when the rails plugin is loaded. They accept the same options as Remote#run . remote.rails(*args, **options) \u2192 Tomo::Result Runs bundle exec rails in within paths.release by default. remote.rails(\"routes\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rails routes remote.rake(*args, **options) \u2192 Tomo::Result Runs bundle exec rake in within paths.release by default. remote.rake(\"db:migrate\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rake db:migrate remote.rake?(*args, **options) \u2192 true or false Like rake but returns true if the command succeeds (exit status 0), otherwise false . remote.rake?(\"db:migrate\") # => true remote.thor(*args, **options) \u2192 Tomo::Result Runs bundle exec thor in within paths.release by default. remote.thor(\"user:create\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec thor user:create","title":"rails"},{"location":"plugins/rails/#rails","text":"The rails plugin provides tasks for running rails and rake commands commonly used during setup and deployment, such as for precompiling assets and migrating the database. Make sure the RAILS_ENV environment variable is set prior to running rails tasks. The env plugin is the preferred mechanism for this.","title":"rails"},{"location":"plugins/rails/#settings","text":"None.","title":"Settings"},{"location":"plugins/rails/#tasks","text":"","title":"Tasks"},{"location":"plugins/rails/#railsassets_precompile","text":"Builds the asset pipeline in preparation for deployment. This is necessary for Rails apps that use the asset pipeline, which is all new Rails apps by default. Running this task will execute this script: cd /var/www/my-app/releases/ && bundle exec rake assets:precompile rails:assets_precompile is intended for use as a deploy task. It is typically run just prior to core:symlink_current to activate a new release.","title":"rails:assets_precompile"},{"location":"plugins/rails/#railsconsole","text":"Starts an interactive Rails console via SSH to the remote host. This task is intended for use as a run task and accepts command-line arguments. The arguments are passed through to the console. For example: $ tomo run -- rails:console --sandbox Will run this remote script: cd /var/www/my-app/current && bundle exec rails console --sandbox","title":"rails:console"},{"location":"plugins/rails/#railsdb_console","text":"Starts an interactive database console (e.g. psql) for the primary Rails database via SSH to the remote host. This task is intended for use as a run task. The include-password option is passed automatically. $ tomo run rails:db_console Will run this remote script: cd /var/www/my-app/current && bundle exec rails dbconsole --include-password","title":"rails:db_console"},{"location":"plugins/rails/#railsdb_migrate","text":"Migrates the database by running: cd /var/www/my-app/releases/ && bundle exec rake db:migrate rails:db_migrate is intended for use as a deploy task. It is typically run just after bundler:install prior to activating a new release.","title":"rails:db_migrate"},{"location":"plugins/rails/#railsdb_seed","text":"Loads seed data into the database. Seeds should be written to be idempotent, such that it is safe to seed the database on each deploy. Typically seeds are used to load reference data need for the app to function, or for example to create an initial admin user. This task runs the following script: cd /var/www/my-app/releases/ && bundle exec rake db:seed rails:db_seed is intended for use as a deploy task. Since seeds rely on the structure of the database, it is typically run just after rails:db_migrate .","title":"rails:db_seed"},{"location":"plugins/rails/#railsdb_create","text":"Runs bundle exec rake db:create to create the database. This task is intended for use as a setup task. It will be automatically skipped if the database already exists, so it is safe to re-run.","title":"rails:db_create"},{"location":"plugins/rails/#railsdb_schema_load","text":"Runs bundle exec rake db:schema:load to load the schema from db/schema.rb into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run.","title":"rails:db_schema_load"},{"location":"plugins/rails/#railsdb_structure_load","text":"Runs bundle exec rake db:structure:load to load the schema from db/structure.sql into an existing database. This task is intended for use as a setup task after rails:db_create . It will be automatically skipped if the database already contains a schema, so it is safe to re-run.","title":"rails:db_structure_load"},{"location":"plugins/rails/#helpers","text":"These helper methods become available on instances of Remote when the rails plugin is loaded. They accept the same options as Remote#run .","title":"Helpers"},{"location":"plugins/rails/#remoterailsargs-options-tomoresult","text":"Runs bundle exec rails in within paths.release by default. remote.rails(\"routes\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rails routes","title":"remote.rails(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/rails/#remoterakeargs-options-tomoresult","text":"Runs bundle exec rake in within paths.release by default. remote.rake(\"db:migrate\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec rake db:migrate","title":"remote.rake(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/rails/#remoterakeargs-options-true-or-false","text":"Like rake but returns true if the command succeeds (exit status 0), otherwise false . remote.rake?(\"db:migrate\") # => true","title":"remote.rake?(*args, **options) \u2192 true or false"},{"location":"plugins/rails/#remotethorargs-options-tomoresult","text":"Runs bundle exec thor in within paths.release by default. remote.thor(\"user:create\") # $ cd /var/www/my-app/releases/20190604204415 && bundle exec thor user:create","title":"remote.thor(*args, **options) \u2192 Tomo::Result"},{"location":"plugins/rbenv/","text":"rbenv The rbenv plugin provides a way to install and run a desired version of ruby. This is the recommended way to manage ruby for Rails apps. Settings Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" rbenv_ruby_version Version of ruby to install; if nil (the default), determine the version based on .ruby-version nil Tasks rbenv:install Installs rbenv, uses rbenv to install ruby, and makes the desired version of ruby the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that rbenv is automatically loaded for interactive and non-interactive shells. Behind the scenes, rbenv installs ruby via ruby-build, which compiles ruby from source. This means installation can take several minutes. If the desired version of ruby is already installed, the compilation step will be skipped. You must supply a value for the rbenv_ruby_version setting or have a .ruby-version file in your project for this task to work. rbenv:install is intended for use as a setup task.","title":"rbenv"},{"location":"plugins/rbenv/#rbenv","text":"The rbenv plugin provides a way to install and run a desired version of ruby. This is the recommended way to manage ruby for Rails apps.","title":"rbenv"},{"location":"plugins/rbenv/#settings","text":"Name Purpose Default bashrc_path Location of the deploy user\u2019s .bashrc file \".bashrc\" rbenv_ruby_version Version of ruby to install; if nil (the default), determine the version based on .ruby-version nil","title":"Settings"},{"location":"plugins/rbenv/#tasks","text":"","title":"Tasks"},{"location":"plugins/rbenv/#rbenvinstall","text":"Installs rbenv, uses rbenv to install ruby, and makes the desired version of ruby the global default version for the deploy user. During installation, the user\u2019s bashrc file is modified so that rbenv is automatically loaded for interactive and non-interactive shells. Behind the scenes, rbenv installs ruby via ruby-build, which compiles ruby from source. This means installation can take several minutes. If the desired version of ruby is already installed, the compilation step will be skipped. You must supply a value for the rbenv_ruby_version setting or have a .ruby-version file in your project for this task to work. rbenv:install is intended for use as a setup task.","title":"rbenv:install"},{"location":"readme_images/","text":"The images in this directory can be regenerated using the record_console.rb script. For example: ./record_console.rb tomo deploy --help This will translate the output of the tomo deploy --help command into HTML and launch it in a browser. From there, press the Convert to PNG button to generate an image appropriately sized for a GitHub README.","title":"Index"},{"location":"tutorials/deploying-rails-from-scratch/","text":"Deploying Rails From Scratch In this tutorial we will use tomo to deploy a sample Rails project to a virtual private server (VPS). These instructions use DigitalOcean as the hosting provider, but any provider that offers an Ubuntu LTS VPS should work in a similar way. Here are the steps involved (step 1 is the only part that is DigitalOcean-specific): Create an Ubuntu VPS Install necessary apt packages Set up a deployer user Configure tomo Run tomo setup Run tomo deploy This is a basic tutorial that skips over DNS, TLS, load balancing, PostgreSQL, etc. If you have suggestions for expanding this guide, consider opening an issue or pull request on GitHub . Thanks for reading! Create an Ubuntu VPS Log into DigitalOcean and create a \u201cDroplet\u201d (aka a VPS). If this is your first time using DigitalOcean, check out their Droplet QuickStart guide for an introduction to the service. When creating the Droplet, make sure to choose Ubuntu 20.04, 22.04, or 24.04 (LTS) x64 : And use SSH keys for authentication (tomo does not work with password authentication): And select a Droplet size with at least 1GB RAM , or Ruby may fail to compile: Once the Droplet is created, confirm that you are able to connect to it (substitute IPADDR with the IP address provided in the DigitalOcean control panel for the new Droplet): # Run on your local machine $ ssh -o PasswordAuthentication=no root@IPADDR echo \"authenticated as root!\" authenticated as root! You may need to type yes when prompted to trust the host fingerprint. Install necessary apt packages Rails requires certain operating system packages in order to build Ruby and install various gems that have native extensions. Connect to the VPS as root and install the following: # Run these commands as root on the VPS apt-get -y update apt-get -y install autoconf \\ bison \\ build-essential \\ curl \\ git-core \\ libdb-dev \\ libffi-dev \\ libgdbm-dev \\ libgdbm6 \\ libgmp-dev \\ libncurses5-dev \\ libreadline6-dev \\ libsqlite3-dev \\ libssl-dev \\ libyaml-dev \\ locales \\ patch \\ pkg-config \\ rustc \\ uuid-dev \\ zlib1g-dev \\ tzdata locale-gen en_US.UTF-8 It may take a minute or two for all the packages to install. Set up a deployer user Running a Rails app as root is a security risk; we need to create a non-privileged user for this purpose. Run the following script to create a deployer user that: has access to write to a /var/www directory, which is the default location where tomo will deploy our app; and can \u201clinger\u201d, i.e. run long-running processes like the puma web server # Run these commands as root on the VPS adduser --disabled-password deployer < /dev/null mkdir -p /home/deployer/.ssh cp /root/.ssh/authorized_keys /home/deployer/.ssh chown -R deployer:deployer /home/deployer/.ssh chmod 600 /home/deployer/.ssh/authorized_keys mkdir -p /var/www chown deployer:deployer /var/www loginctl enable-linger deployer For convenience, the deployer user will accept the same SSH key that you are already using to authenticate when connecting as root . Test that it works: # Run on your local machine $ ssh -o PasswordAuthentication=no deployer@IPADDR echo \"authenticated as deployer!\" authenticated as deployer! Configure tomo We will be deploying this basic Rails app . It is a standard Rails app generated by rails new , with the exception that force_ssl has been set to false for the production environment. This will allow us to deploy the app without having to set up HTTPS, which is outside the scope of this tutorial. Clone the repository to get started: # Run on your local machine $ git clone https://github.com/mattbrictson/rails-new Inside the rails-new directory, install tomo and run tomo init . This will set up a deploy configuration with a good set of defaults: # Run on your local machine $ cd rails-new $ gem install tomo Fetching tomo-1.11.0.gem Successfully installed tomo-1.11.0 1 gem installed $ tomo init \u2714 Created .tomo/config.rb The .tomo/config.rb configuration is ready right out of the box. All you will need to change is the host line, and be sure to replace IPADDR with the IP address of your VPS: # Modify the 'host' line of .tomo/config.rb host \"deployer@IPADDR\" Run tomo setup Tomo comes with a setup command that will prepare your VPS for its first deployment. This does things like install Node, Yarn, and Ruby. It also sets up some important environment variables. Just run: # Run on your local machine $ tomo setup You will be asked to specify a value for the DATABASE_URL environment variable. Tomo will store this value on the VPS so that you only have to provide it once. Provide this value when prompted: sqlite3:/var/www/rails-new/shared/production.sqlite3 . Note that tomo setup compiles Ruby from source, which will take several minutes. Run tomo deploy Once setup completes, your VPS is ready to go. Deploying is now just a matter of running tomo deploy : # Run on your local machine $ tomo deploy When the deploy completes, the Rails app will be accessible on port 3000 of your VPS: http://IPADDR:3000 . Congratulations!","title":"Deploying Rails From Scratch"},{"location":"tutorials/deploying-rails-from-scratch/#deploying-rails-from-scratch","text":"In this tutorial we will use tomo to deploy a sample Rails project to a virtual private server (VPS). These instructions use DigitalOcean as the hosting provider, but any provider that offers an Ubuntu LTS VPS should work in a similar way. Here are the steps involved (step 1 is the only part that is DigitalOcean-specific): Create an Ubuntu VPS Install necessary apt packages Set up a deployer user Configure tomo Run tomo setup Run tomo deploy This is a basic tutorial that skips over DNS, TLS, load balancing, PostgreSQL, etc. If you have suggestions for expanding this guide, consider opening an issue or pull request on GitHub . Thanks for reading!","title":"Deploying Rails From Scratch"},{"location":"tutorials/deploying-rails-from-scratch/#create-an-ubuntu-vps","text":"Log into DigitalOcean and create a \u201cDroplet\u201d (aka a VPS). If this is your first time using DigitalOcean, check out their Droplet QuickStart guide for an introduction to the service. When creating the Droplet, make sure to choose Ubuntu 20.04, 22.04, or 24.04 (LTS) x64 : And use SSH keys for authentication (tomo does not work with password authentication): And select a Droplet size with at least 1GB RAM , or Ruby may fail to compile: Once the Droplet is created, confirm that you are able to connect to it (substitute IPADDR with the IP address provided in the DigitalOcean control panel for the new Droplet): # Run on your local machine $ ssh -o PasswordAuthentication=no root@IPADDR echo \"authenticated as root!\" authenticated as root! You may need to type yes when prompted to trust the host fingerprint.","title":"Create an Ubuntu VPS"},{"location":"tutorials/deploying-rails-from-scratch/#install-necessary-apt-packages","text":"Rails requires certain operating system packages in order to build Ruby and install various gems that have native extensions. Connect to the VPS as root and install the following: # Run these commands as root on the VPS apt-get -y update apt-get -y install autoconf \\ bison \\ build-essential \\ curl \\ git-core \\ libdb-dev \\ libffi-dev \\ libgdbm-dev \\ libgdbm6 \\ libgmp-dev \\ libncurses5-dev \\ libreadline6-dev \\ libsqlite3-dev \\ libssl-dev \\ libyaml-dev \\ locales \\ patch \\ pkg-config \\ rustc \\ uuid-dev \\ zlib1g-dev \\ tzdata locale-gen en_US.UTF-8 It may take a minute or two for all the packages to install.","title":"Install necessary apt packages"},{"location":"tutorials/deploying-rails-from-scratch/#set-up-a-deployer-user","text":"Running a Rails app as root is a security risk; we need to create a non-privileged user for this purpose. Run the following script to create a deployer user that: has access to write to a /var/www directory, which is the default location where tomo will deploy our app; and can \u201clinger\u201d, i.e. run long-running processes like the puma web server # Run these commands as root on the VPS adduser --disabled-password deployer < /dev/null mkdir -p /home/deployer/.ssh cp /root/.ssh/authorized_keys /home/deployer/.ssh chown -R deployer:deployer /home/deployer/.ssh chmod 600 /home/deployer/.ssh/authorized_keys mkdir -p /var/www chown deployer:deployer /var/www loginctl enable-linger deployer For convenience, the deployer user will accept the same SSH key that you are already using to authenticate when connecting as root . Test that it works: # Run on your local machine $ ssh -o PasswordAuthentication=no deployer@IPADDR echo \"authenticated as deployer!\" authenticated as deployer!","title":"Set up a deployer user"},{"location":"tutorials/deploying-rails-from-scratch/#configure-tomo","text":"We will be deploying this basic Rails app . It is a standard Rails app generated by rails new , with the exception that force_ssl has been set to false for the production environment. This will allow us to deploy the app without having to set up HTTPS, which is outside the scope of this tutorial. Clone the repository to get started: # Run on your local machine $ git clone https://github.com/mattbrictson/rails-new Inside the rails-new directory, install tomo and run tomo init . This will set up a deploy configuration with a good set of defaults: # Run on your local machine $ cd rails-new $ gem install tomo Fetching tomo-1.11.0.gem Successfully installed tomo-1.11.0 1 gem installed $ tomo init \u2714 Created .tomo/config.rb The .tomo/config.rb configuration is ready right out of the box. All you will need to change is the host line, and be sure to replace IPADDR with the IP address of your VPS: # Modify the 'host' line of .tomo/config.rb host \"deployer@IPADDR\"","title":"Configure tomo"},{"location":"tutorials/deploying-rails-from-scratch/#run-tomo-setup","text":"Tomo comes with a setup command that will prepare your VPS for its first deployment. This does things like install Node, Yarn, and Ruby. It also sets up some important environment variables. Just run: # Run on your local machine $ tomo setup You will be asked to specify a value for the DATABASE_URL environment variable. Tomo will store this value on the VPS so that you only have to provide it once. Provide this value when prompted: sqlite3:/var/www/rails-new/shared/production.sqlite3 . Note that tomo setup compiles Ruby from source, which will take several minutes.","title":"Run tomo setup"},{"location":"tutorials/deploying-rails-from-scratch/#run-tomo-deploy","text":"Once setup completes, your VPS is ready to go. Deploying is now just a matter of running tomo deploy : # Run on your local machine $ tomo deploy When the deploy completes, the Rails app will be accessible on port 3000 of your VPS: http://IPADDR:3000 . Congratulations!","title":"Run tomo deploy"},{"location":"tutorials/publishing-a-plugin/","text":"Publishing a Plugin In this tutorial we will package a tomo plugin so that it can be shared with the community as a Ruby gem. Here is a quick reference to the steps involved in creating a plugin gem: Start with a project-based plugin to prove real-world usage Use the tomo-plugin GitHub template to package your plugin as a Ruby gem Write unit tests for your tasks using MockPluginTester Add docs and continuous integration (CI) to help your users and contributors Publish to rubygems.org To keep things simple, we\u2019ll package the example cron plugin that we created in the Writing Custom Tasks tutorial . Check out that article for more details on writing plugins and tasks. Here we will focus just on the gem packaging part. Create the gem While it is easy to get started writing tasks with a project-based plugin, this ad-hoc style does not have test coverage and is not packaged in a way that makes it easy to share with other projects and the larger tomo community. This is why it is useful to package your plugin in its own Ruby gem project. Choose a name Before packaging your plugin, choose a name. Your gem will be named tomo-plugin-NAME where NAME is the name of your plugin. Decide on a plugin name that is concise and whose gem name is not already taken at rubygems.org . For example the \u201csidekiq\u201d plugin corresponds to the tomo-plugin-sidekiq gem at rubygems.org. Use the tomo-plugin template Tomo provides a GitHub template that does most of the work of setting up the gem project. Navigate to the tomo-plugin template on GitHub Press Use this template Name the repo tomo-plugin-NAME where NAME is the name of the plugin For this example we will use \u201ccron\u201d as the plugin name. Run the rename_template.rb script Use git to clone the resulting GitHub repo. Inside the repo, run: $ ruby rename_template.rb This will prompt for some information needed to set up the project. Press enter to accept the default values, which should be sufficient for each question. >>>> git remote -v >>>> git remote get-url origin >>>> git config user.email >>>> git config user.name Plugin name? [cron] Gem summary (< 60 chars)? [cron tasks for tomo] Author email? [opensource@mattbrictson.com] Author name? [Matt Brictson] GitHub repository? [mattbrictson/tomo-plugin-cron] Create GitHub labels? [Y/n] I need to ask for your GitHub credentials in order to create labels. Don't worry, your GitHub credentials will NOT be saved. GitHub username? [mattbrictson] GitHub password? 2FA token? 012345 Created labels: \u26a0\ufe0f Breaking, \ud83d\udc1b Bug Fix, \ud83d\udcda Docs, \u2728 Feature, \ud83c\udfe0 Housekeeping >>>> git mv .github/workflows/push.yml.dist .github/workflows/push.yml >>>> git add LICENSE.txt >>>> git add Rakefile >>>> git add README.md >>>> git add CHANGELOG.md >>>> git add CODE_OF_CONDUCT.md >>>> git add bin/console >>>> git add example.gemspec >>>> git mv example.gemspec tomo-plugin-cron.gemspec >>>> git add lib/tomo/plugin/example.rb >>>> git mv lib/tomo/plugin/example.rb lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git add lib/tomo/plugin/example/helpers.rb >>>> git mv lib/tomo/plugin/example/helpers.rb lib/tomo/plugin/cron/helpers.rb >>>> git add lib/tomo/plugin/example/tasks.rb >>>> git mv lib/tomo/plugin/example/tasks.rb lib/tomo/plugin/cron/tasks.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git mv lib/tomo/plugin/example/version.rb lib/tomo/plugin/cron/version.rb >>>> git add lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/cron/version.rb >>>> git add test/tomo/plugin/example_test.rb >>>> git mv test/tomo/plugin/example_test.rb test/tomo/plugin/cron_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git mv test/tomo/plugin/example/helpers_test.rb test/tomo/plugin/cron/helpers_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git mv test/tomo/plugin/example/tasks_test.rb test/tomo/plugin/cron/tasks_test.rb >>>> git add test/test_helper.rb >>>> git rm rename_template.rb All set! The project has been renamed to \"tomo-plugin-cron\". Review the changes and then run: git commit && git push Don\u2019t forget to git commit and git push the changes. Define the plugin The entry point for a tomo plugin gem is the lib/tomo/plugin/.rb file. This file defines the plugin by extending Tomo::PluginDSL to provide the following three pieces of information: Default settings Tasks Helpers For our \u201ccron\u201d plugin, the entry point generated from the template looks like this: # lib/tomo/plugin/cron.rb require \"tomo\" require_relative \"cron/helpers\" require_relative \"cron/tasks\" require_relative \"cron/version\" module Tomo::Plugin::Cron extend Tomo::PluginDSL # TODO: initialize this plugin's settings with default values # defaults cron_setting: \"foo\", # cron_another_setting: \"bar\" tasks Tomo::Plugin::Cron::Tasks helpers Tomo::Plugin::Cron::Helpers end Default settings It is a good practice to provide default values for all settings that your plugin relies on. If there is truly no default value for a setting, declare it as nil . This at least informs tomo the name of the setting that the plugin is expecting. Our cron plugin needs a crontab template that has no default value (the user of the plugin will need to provide it): defaults cron_template_path: nil Notice how we\u2019ve named the setting starting with cron_ . This is a convention that makes it clear to users which settings correspond to which plugin, and reduces the unintended collisions. Tasks By default, the tasks of the plugin are defined in a file lib/tomo/plugin//tasks.rb . It looks like this: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary # tasks go here end end We can copy the tasks from the Writing Custom Tasks tutorial into this file with a few modifications to use the new :cron_template_path setting we just defined: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary def show remote.run \"crontab -l\", raise_on_error: false end def install require_setting :cron_template_path crontab = merge_template(settings[:cron_template_path]) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end end end Note the require_setting line will check that the user has provided a value for the :cron_template_path setting and will show a helpful error message if it is missing. Helpers Helpers extend the remote DSL in tomo. This is an advanced feature that most plugins will not use. We can remove it from our plugin: Delete lib/tomo/plugin/cron/helpers.rb Delete test/tomo/plugin/cron/helpers_test.rb Remove the following lines from lib/tomo/plugin/cron.rb require_relative \"cron/helpers\" helpers Tomo::Plugin::Cron::Helpers Write tests Tomo makes it easy to write tests in a plugin gem using the MockPluginTester . MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. Here\u2019s what tests look like for the tasks of our hypothetical cron plugin: # test/tomo/plugin/cron/tasks_test.rb require \"test_helper\" require \"tempfile\" class Tomo::Plugin::Cron::TasksTest < Minitest::Test def test_show tester = Tomo::Testing::MockPluginTester.new(\"cron\") tester.run_task(\"cron:show\") assert_equal(\"crontab -l\", tester.executed_script) end def test_install # Create a sample crontab template for use in the test crontab = \"0 6 * * * echo hi\\n\" crontab_file = Tempfile.new crontab_file << crontab crontab_file.close tester = Tomo::Testing::MockPluginTester.new( \"cron\", settings: { cron_template_path: crontab_file.path } ) tester.run_task(\"cron:install\") assert_equal( \"echo #{crontab.shellescape} | crontab -\", tester.executed_script ) end end To run the tests: $ bundle exec rake Started with run options --seed 29280 Tomo::Plugin::CronTest test_that_it_has_a_version_number PASS (0.00s) Tomo::Plugin::Cron::TasksTest test_install PASS (0.03s) test_show PASS (0.00s) Finished in 0.03056s 3 tests, 3 assertions, 0 failures, 0 errors, 0 skips Running RuboCop... Inspecting 12 files ............ 12 files inspected, no offenses detected Try it out Unit tests are valuable but no substitute for trying out your plugin gem in a real-world scenario. There are two easy ways to try your gem before publishing. One option is to install the gem into your local Ruby environment like this: $ bundle exec rake install Now your plugin will be available anywhere you use tomo on your local system. However, that won\u2019t work for a project where you are managing tomo gems via Bundler. In that case, edit the Gemfile of the project to reference your plugin by path : gem \"tomo-plugin-cron\", path: \"../tomo-plugin-cron\" Run bundle install and now your plugin will be available via bundle exec tomo . Write a good README The tomo-plugin template will create a basic README but it is up to you to fill in the details. In particular users will be interested to know what tasks your plugin provides and how to configure it. Make sure to fill in the Settings and Tasks sections of the README. In the case of our hypothetical cron plugin, we\u2019d want to specify installation instructions as well, to explain that the cron:install task should be listed part of the setup tasks. Check out tomo-plugin-sidekiq for an example README. Set up CI The tomo-plugin template will set your gem project up with Travis, Circle CI, and Code Climate out of the box, but it is up to you to log into those services and turn them on. Here are some links to get you started: Circle CI : Log in via your GitHub account and then click Add Projects . Code Climate : Log into the Quality product via your GitHub account, click Open Source , then Add a repository . Travis CI : Log into travis-ci.com via your GitHub account, go to Account Settings , then enable your gem project in the Repositories section. These services will build and test your project on every push and PR and update the status badges in your README. Publish to rubygems.org Once you are ready to share your gem with the world, create a rubygems.org account if you haven\u2019t already. Then ensure you are logged in via the gem CLI: $ gem signin To publish, run: $ bundle exec rake release Finally, don\u2019t forget to document your release with release notes in the Releases section of your project on GitHub. Releasing a new version If you\u2019ve already published your gem and want to release a new version, first edit the version.rb file: # lib/tomo/plugin/cron/version.rb module Tomo::Plugin::Cron VERSION = \"0.2.0\".freeze end Commit the changes: $ git commit -a -m \"Release v0.2.0\" And then release: $ bundle exec rake release Once again, don\u2019t forget the release notes on GitHub. That\u2019s it!","title":"Publishing a Plugin"},{"location":"tutorials/publishing-a-plugin/#publishing-a-plugin","text":"In this tutorial we will package a tomo plugin so that it can be shared with the community as a Ruby gem. Here is a quick reference to the steps involved in creating a plugin gem: Start with a project-based plugin to prove real-world usage Use the tomo-plugin GitHub template to package your plugin as a Ruby gem Write unit tests for your tasks using MockPluginTester Add docs and continuous integration (CI) to help your users and contributors Publish to rubygems.org To keep things simple, we\u2019ll package the example cron plugin that we created in the Writing Custom Tasks tutorial . Check out that article for more details on writing plugins and tasks. Here we will focus just on the gem packaging part.","title":"Publishing a Plugin"},{"location":"tutorials/publishing-a-plugin/#create-the-gem","text":"While it is easy to get started writing tasks with a project-based plugin, this ad-hoc style does not have test coverage and is not packaged in a way that makes it easy to share with other projects and the larger tomo community. This is why it is useful to package your plugin in its own Ruby gem project.","title":"Create the gem"},{"location":"tutorials/publishing-a-plugin/#choose-a-name","text":"Before packaging your plugin, choose a name. Your gem will be named tomo-plugin-NAME where NAME is the name of your plugin. Decide on a plugin name that is concise and whose gem name is not already taken at rubygems.org . For example the \u201csidekiq\u201d plugin corresponds to the tomo-plugin-sidekiq gem at rubygems.org.","title":"Choose a name"},{"location":"tutorials/publishing-a-plugin/#use-the-tomo-plugin-template","text":"Tomo provides a GitHub template that does most of the work of setting up the gem project. Navigate to the tomo-plugin template on GitHub Press Use this template Name the repo tomo-plugin-NAME where NAME is the name of the plugin For this example we will use \u201ccron\u201d as the plugin name.","title":"Use the tomo-plugin template"},{"location":"tutorials/publishing-a-plugin/#run-the-rename_templaterb-script","text":"Use git to clone the resulting GitHub repo. Inside the repo, run: $ ruby rename_template.rb This will prompt for some information needed to set up the project. Press enter to accept the default values, which should be sufficient for each question. >>>> git remote -v >>>> git remote get-url origin >>>> git config user.email >>>> git config user.name Plugin name? [cron] Gem summary (< 60 chars)? [cron tasks for tomo] Author email? [opensource@mattbrictson.com] Author name? [Matt Brictson] GitHub repository? [mattbrictson/tomo-plugin-cron] Create GitHub labels? [Y/n] I need to ask for your GitHub credentials in order to create labels. Don't worry, your GitHub credentials will NOT be saved. GitHub username? [mattbrictson] GitHub password? 2FA token? 012345 Created labels: \u26a0\ufe0f Breaking, \ud83d\udc1b Bug Fix, \ud83d\udcda Docs, \u2728 Feature, \ud83c\udfe0 Housekeeping >>>> git mv .github/workflows/push.yml.dist .github/workflows/push.yml >>>> git add LICENSE.txt >>>> git add Rakefile >>>> git add README.md >>>> git add CHANGELOG.md >>>> git add CODE_OF_CONDUCT.md >>>> git add bin/console >>>> git add example.gemspec >>>> git mv example.gemspec tomo-plugin-cron.gemspec >>>> git add lib/tomo/plugin/example.rb >>>> git mv lib/tomo/plugin/example.rb lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git add lib/tomo/plugin/example/helpers.rb >>>> git mv lib/tomo/plugin/example/helpers.rb lib/tomo/plugin/cron/helpers.rb >>>> git add lib/tomo/plugin/example/tasks.rb >>>> git mv lib/tomo/plugin/example/tasks.rb lib/tomo/plugin/cron/tasks.rb >>>> git add lib/tomo/plugin/example/version.rb >>>> git mv lib/tomo/plugin/example/version.rb lib/tomo/plugin/cron/version.rb >>>> git add lib/tomo/plugin/cron.rb >>>> git add lib/tomo/plugin/cron/version.rb >>>> git add test/tomo/plugin/example_test.rb >>>> git mv test/tomo/plugin/example_test.rb test/tomo/plugin/cron_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git add test/tomo/plugin/example/helpers_test.rb >>>> git mv test/tomo/plugin/example/helpers_test.rb test/tomo/plugin/cron/helpers_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git add test/tomo/plugin/example/tasks_test.rb >>>> git mv test/tomo/plugin/example/tasks_test.rb test/tomo/plugin/cron/tasks_test.rb >>>> git add test/test_helper.rb >>>> git rm rename_template.rb All set! The project has been renamed to \"tomo-plugin-cron\". Review the changes and then run: git commit && git push Don\u2019t forget to git commit and git push the changes.","title":"Run the rename_template.rb script"},{"location":"tutorials/publishing-a-plugin/#define-the-plugin","text":"The entry point for a tomo plugin gem is the lib/tomo/plugin/.rb file. This file defines the plugin by extending Tomo::PluginDSL to provide the following three pieces of information: Default settings Tasks Helpers For our \u201ccron\u201d plugin, the entry point generated from the template looks like this: # lib/tomo/plugin/cron.rb require \"tomo\" require_relative \"cron/helpers\" require_relative \"cron/tasks\" require_relative \"cron/version\" module Tomo::Plugin::Cron extend Tomo::PluginDSL # TODO: initialize this plugin's settings with default values # defaults cron_setting: \"foo\", # cron_another_setting: \"bar\" tasks Tomo::Plugin::Cron::Tasks helpers Tomo::Plugin::Cron::Helpers end","title":"Define the plugin"},{"location":"tutorials/publishing-a-plugin/#default-settings","text":"It is a good practice to provide default values for all settings that your plugin relies on. If there is truly no default value for a setting, declare it as nil . This at least informs tomo the name of the setting that the plugin is expecting. Our cron plugin needs a crontab template that has no default value (the user of the plugin will need to provide it): defaults cron_template_path: nil Notice how we\u2019ve named the setting starting with cron_ . This is a convention that makes it clear to users which settings correspond to which plugin, and reduces the unintended collisions.","title":"Default settings"},{"location":"tutorials/publishing-a-plugin/#tasks","text":"By default, the tasks of the plugin are defined in a file lib/tomo/plugin//tasks.rb . It looks like this: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary # tasks go here end end We can copy the tasks from the Writing Custom Tasks tutorial into this file with a few modifications to use the new :cron_template_path setting we just defined: # lib/tomo/plugin/cron/tasks.rb module Tomo::Plugin::Cron class Tasks < Tomo::TaskLibrary def show remote.run \"crontab -l\", raise_on_error: false end def install require_setting :cron_template_path crontab = merge_template(settings[:cron_template_path]) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end end end Note the require_setting line will check that the user has provided a value for the :cron_template_path setting and will show a helpful error message if it is missing.","title":"Tasks"},{"location":"tutorials/publishing-a-plugin/#helpers","text":"Helpers extend the remote DSL in tomo. This is an advanced feature that most plugins will not use. We can remove it from our plugin: Delete lib/tomo/plugin/cron/helpers.rb Delete test/tomo/plugin/cron/helpers_test.rb Remove the following lines from lib/tomo/plugin/cron.rb require_relative \"cron/helpers\" helpers Tomo::Plugin::Cron::Helpers","title":"Helpers"},{"location":"tutorials/publishing-a-plugin/#write-tests","text":"Tomo makes it easy to write tests in a plugin gem using the MockPluginTester . MockPluginTester works by mocking the underlying SSH connection so that no actual remote SSH scripts are run. By default, the tester will simulate that the script runs successfully (exit status of 0) with empty stdout and stderr. You can then write assertions verifying that the script was run as expected. Here\u2019s what tests look like for the tasks of our hypothetical cron plugin: # test/tomo/plugin/cron/tasks_test.rb require \"test_helper\" require \"tempfile\" class Tomo::Plugin::Cron::TasksTest < Minitest::Test def test_show tester = Tomo::Testing::MockPluginTester.new(\"cron\") tester.run_task(\"cron:show\") assert_equal(\"crontab -l\", tester.executed_script) end def test_install # Create a sample crontab template for use in the test crontab = \"0 6 * * * echo hi\\n\" crontab_file = Tempfile.new crontab_file << crontab crontab_file.close tester = Tomo::Testing::MockPluginTester.new( \"cron\", settings: { cron_template_path: crontab_file.path } ) tester.run_task(\"cron:install\") assert_equal( \"echo #{crontab.shellescape} | crontab -\", tester.executed_script ) end end To run the tests: $ bundle exec rake Started with run options --seed 29280 Tomo::Plugin::CronTest test_that_it_has_a_version_number PASS (0.00s) Tomo::Plugin::Cron::TasksTest test_install PASS (0.03s) test_show PASS (0.00s) Finished in 0.03056s 3 tests, 3 assertions, 0 failures, 0 errors, 0 skips Running RuboCop... Inspecting 12 files ............ 12 files inspected, no offenses detected","title":"Write tests"},{"location":"tutorials/publishing-a-plugin/#try-it-out","text":"Unit tests are valuable but no substitute for trying out your plugin gem in a real-world scenario. There are two easy ways to try your gem before publishing. One option is to install the gem into your local Ruby environment like this: $ bundle exec rake install Now your plugin will be available anywhere you use tomo on your local system. However, that won\u2019t work for a project where you are managing tomo gems via Bundler. In that case, edit the Gemfile of the project to reference your plugin by path : gem \"tomo-plugin-cron\", path: \"../tomo-plugin-cron\" Run bundle install and now your plugin will be available via bundle exec tomo .","title":"Try it out"},{"location":"tutorials/publishing-a-plugin/#write-a-good-readme","text":"The tomo-plugin template will create a basic README but it is up to you to fill in the details. In particular users will be interested to know what tasks your plugin provides and how to configure it. Make sure to fill in the Settings and Tasks sections of the README. In the case of our hypothetical cron plugin, we\u2019d want to specify installation instructions as well, to explain that the cron:install task should be listed part of the setup tasks. Check out tomo-plugin-sidekiq for an example README.","title":"Write a good README"},{"location":"tutorials/publishing-a-plugin/#set-up-ci","text":"The tomo-plugin template will set your gem project up with Travis, Circle CI, and Code Climate out of the box, but it is up to you to log into those services and turn them on. Here are some links to get you started: Circle CI : Log in via your GitHub account and then click Add Projects . Code Climate : Log into the Quality product via your GitHub account, click Open Source , then Add a repository . Travis CI : Log into travis-ci.com via your GitHub account, go to Account Settings , then enable your gem project in the Repositories section. These services will build and test your project on every push and PR and update the status badges in your README.","title":"Set up CI"},{"location":"tutorials/publishing-a-plugin/#publish-to-rubygemsorg","text":"Once you are ready to share your gem with the world, create a rubygems.org account if you haven\u2019t already. Then ensure you are logged in via the gem CLI: $ gem signin To publish, run: $ bundle exec rake release Finally, don\u2019t forget to document your release with release notes in the Releases section of your project on GitHub.","title":"Publish to rubygems.org"},{"location":"tutorials/publishing-a-plugin/#releasing-a-new-version","text":"If you\u2019ve already published your gem and want to release a new version, first edit the version.rb file: # lib/tomo/plugin/cron/version.rb module Tomo::Plugin::Cron VERSION = \"0.2.0\".freeze end Commit the changes: $ git commit -a -m \"Release v0.2.0\" And then release: $ bundle exec rake release Once again, don\u2019t forget the release notes on GitHub. That\u2019s it!","title":"Releasing a new version"},{"location":"tutorials/writing-custom-tasks/","text":"Writing Custom Tasks In this tutorial we will build a \u201ccron\u201d plugin to demonstrate how to write custom tasks in tomo. Here are the main takeaways: Use .tomo/plugins/*.rb to define plugins A task is any public Ruby method within a plugin Tasks can access the TaskLibrary API Use remote.run to execute scripts on the remote host Here\u2019s the final product: # .tomo/config.rb plugin \"./plugins/cron.rb\" # .tomo/plugins/cron.rb def show remote.run \"crontab -l\", raise_on_error: false end def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Before we get there, let\u2019s review the basics. What is a plugin? Plugins extend tomo by providing some or all of these three things: tasks, helpers, and default settings. Plugins are either built into tomo (e.g. git , rails ), provided by gems (e.g. tomo-plugin-sidekiq ), or loaded from ./tomo/plugins/*.rb within a tomo project. This tutorial will focus on project-specific plugins, which are the easiest to write. Once you are ready to share your plugin amongst multiple projects (or with the larger tomo community), check out the Publishing a Plugin tutorial to learn how to package a plugin as a gem. What is a task? In tomo, a task is a plain Ruby method provided by a plugin. Task methods take zero arguments. Here is a trivial example: # .tomo/plugins/foo.rb # This defines a foo:hello task def hello logger.info \"hello, world!\" end The name of the plugin is set automatically based on the name of the .rb file, which in this case is \u201cfoo\u201d. Any public method defined in foo.rb becomes a tomo task. So the example above defines a foo:hello task that prints \u201chello, world!\u201d to the console. What can a task do? Behind the scenes, the Ruby methods you define in your plugin are actually methods on a subclass of TaskLibrary . That means you have full access to the TaskLibrary API within your task method, which includes: logger for printing output (as seen in the example above) remote for running scripts on the remote host settings for accessing project configuration paths for convenient access to filesystem paths on the remote host and more\u2026 For example, a simplified, annotated version of the git:clone task that is built into tomo looks like this: def clone # Halt tomo with an error message if the :git_url setting is nil/unspecified require_setting :git_url # Run \"mkdir -p\" on the remote host to create the parent directory # of the :git_repo_path setting remote.mkdir_p(paths.git_repo.dirname) # Run \"git clone ...\" on the remote host to clone the repo into :git_repo_path remote.run(\"git\", \"clone\", \"--mirror\", settings[:git_url], paths.git_repo) end When are tasks run? Tomo does not have hooks and tasks cannot invoke other tasks. That means that tasks only run when explicitly requested by the user. There are three ways to run a task: deploy setup run For deploy and setup, users invoke your task by including it in the deploy or setup list of tasks in .tomo/config.rb . Additionally, any task can be run on-demand from the command line, like this: $ tomo run foo:hello In the command line case, users can optionally pass arguments to a task. These arguments become available to the task via the :run_args setting. For example, the rails:console task supports command line arguments like this: def console # If this task is run like `tomo run -- rails:console --sandbox` # then settings[:run_args] will be [\"--sandbox\"] args = settings[:run_args] remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"rails\", \"console\", *args, attach: true) end end How do tasks connect to remote hosts? Notice that none of the examples in this tutorial makes any mention of opening/closing connections or specifying hosts or roles. That is because tomo takes care of connecting to remote hosts and automatically decides which tasks should run on which hosts based on project configuration. By the time a task method is invoked, any necessary SSH connection is already established; remote implicitly refers to that connection. In other words, as a tomo task author you only need to be concerned about what remote scripts to run, not where or how they are executed. For a more in-depth explanation of how configuration drives tomo\u2019s behavior, refer to the configuration docs . Tutorial Let\u2019s build something using this knowledge of how tomo tasks work. Objective Say we have a Rails app that needs to run code \u2013 PeriodicTask.call , for example \u2013 every day at 06:00. We\u2019d like to do this with a cron job and use tomo to install that cron job on the remote host. For troubleshooting purposes it would be nice to view the list of cron jobs with tomo as well. That sounds like two distinct tomo tasks: cron:install to install the cron job cron:show to list the currently installed cron jobs We want cron:install to be run when we initially set up the remote host. In other words, it should run as part of tomo setup . On the other hand, cron:show is a utility that we can use on the CLI when needed. cron:show We\u2019ll start by building the simpler of the two tasks: cron:show . First, let\u2019s try to run that task: $ tomo run cron:show tomo run v1.0.0 ERROR: cron:show is not a recognized task. To see a list of all available tasks, run tomo tasks. We haven\u2019t written the task yet, so this error makes sense. Let\u2019s build a skeleton of the cron:show task to fix this error. Create a .tomo/plugins/cron.rb task like this: # .tomo/plugins/cron.rb def show logger.info \"Hi\" end And don\u2019t forget to load the plugin in .tomo/config.rb : # .tomo/config.rb plugin \"./plugins/cron.rb\" Now we can try again: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show Hi \u2714 Ran cron:show on deployer@app.example.com Great! To get a list of cron jobs, we need to run crontab -l on the remote host: def show remote.run \"crontab -l\" end One more try: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer ERROR: The following script failed on deployer@app.example.com (exit status 1). crontab -l You can manually re-execute the script via SSH as follows: ssh -o LogLevel\\=ERROR -A -o ConnectTimeout\\=5 -o StrictHostKeyChecking\\=accept-new -o ControlMaster\\=auto -o ControlPath\\=/var/folders/_v/j_5kgc6n1nz5pb7kfkzz3r5c0000gn/T/tomo_ssh_1f061db77f81ae9e -o ControlPersist\\=30s -o PasswordAuthentication\\=no deployer@app.example.com -- crontab\\ -l For more troubleshooting info, run tomo again using the --debug option. no crontab for deployer Uh oh. There are no cron jobs installed yet, so crontab -l exits with an error. By default, tomo assumes that any remote command the exits with an error status is considered fatal. In this case we just want to see the error output from the crontab command and continue without complaint; that\u2019s where the raise_on_error: false option comes into play: def show remote.run \"crontab -l\", raise_on_error: false end Now we\u2019re all good: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer \u2714 Ran cron:show on deployer@app.example.com cron:install Before we said that we want a cron:install task that runs as part of tomo setup . Let\u2019s start by adding that task to the list of setup tasks in .tomo/config.rb : # .tomo/config.rb setup do # ... other tasks omitted for brevity run \"cron:install\" end If we try to run tomo setup at this point, we\u2019ll get an error as expected: $ tomo setup tomo setup v1.0.0 ERROR: cron:install is not a recognized task. To see a list of all available tasks, run tomo tasks. Did you mean rbenv:install? Cron jobs can be installed by piping a list of cron definitions to crontab - (the - means to read the definitions from stdin). We can take advantage of this to write a simple cron:install task: def install crontab = <<~CRONTAB SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /var/www/my-app/current; bundle exec rails runner PeriodicTask.call > /var/www/my-app/shared/log/periodic-task.log 2>&1 CRONTAB remote.run \"echo #{crontab.shellescape} | crontab -\" end Note that we are using shellescape as part of Ruby\u2019s built-in shellwords library to safely build the script. We can see what this task does without actually affecting the remote host by using --dry-run option: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Looks good! But we if we made it more powerful with some ERB templating? Templates Tomo offers a convenient way to use ERB templates with it\u2019s built-in merge_template and write methods. We can use merge_template instead of a hard-coding the cron job: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\" end The ERB template has access to all the same APIs as our task methods; that means we can remove the hard-coded paths from our original cron job specification and use tomo\u2019s paths helper. So our ERB template file ( .tomo/templates/crontab.erb ) could look like this: # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Let\u2019s check that it still works: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) That\u2019s great, but the output is really verbose. Do we really need to see the full contents of the crontab being echoed? What if our template becomes really large? In tomo, you can mute this output using echo: false , but you can also provide an echo string to show instead of the command. We can use this to echo an abbreviated version: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end And then try it: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo [template:.tomo/templates/crontab.erb] | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Ah, much cleaner! The result We now have a cron:install task that will automatically run as part of tomo setup , or can be run manually using tomo run cron:install . Let\u2019s try it for real: $ tomo setup ... [snip] ... \u2022 cron:install echo [template:.tomo/templates/crontab.erb] | crontab - \u2714 Performed setup of my-app on deployer@app.example.com And we can see what is installed with our cron:show task: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /home/deployer/apps/my-app/current; bundle exec rails runner PeriodicTask.call > /home/deployer/apps/my-app/shared/log/periodic-task.log 2>&1 \u2714 Ran cron:show on deployer@app.example.com Next steps This tutorial introduced you to writing custom tasks in tomo, but there is much more to explore. For next steps, check out these APIs: Tomo::TaskLibrary Tomo::Remote Tomo::Result Tomo::Paths core plugin helpers (additional methods mixed into the Remote API) And for inspiration, look no further than tomo itself, which has several built-in plugins in lib/tomo/plugin .","title":"Writing Custom Tasks"},{"location":"tutorials/writing-custom-tasks/#writing-custom-tasks","text":"In this tutorial we will build a \u201ccron\u201d plugin to demonstrate how to write custom tasks in tomo. Here are the main takeaways: Use .tomo/plugins/*.rb to define plugins A task is any public Ruby method within a plugin Tasks can access the TaskLibrary API Use remote.run to execute scripts on the remote host Here\u2019s the final product: # .tomo/config.rb plugin \"./plugins/cron.rb\" # .tomo/plugins/cron.rb def show remote.run \"crontab -l\", raise_on_error: false end def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Before we get there, let\u2019s review the basics.","title":"Writing Custom Tasks"},{"location":"tutorials/writing-custom-tasks/#what-is-a-plugin","text":"Plugins extend tomo by providing some or all of these three things: tasks, helpers, and default settings. Plugins are either built into tomo (e.g. git , rails ), provided by gems (e.g. tomo-plugin-sidekiq ), or loaded from ./tomo/plugins/*.rb within a tomo project. This tutorial will focus on project-specific plugins, which are the easiest to write. Once you are ready to share your plugin amongst multiple projects (or with the larger tomo community), check out the Publishing a Plugin tutorial to learn how to package a plugin as a gem.","title":"What is a plugin?"},{"location":"tutorials/writing-custom-tasks/#what-is-a-task","text":"In tomo, a task is a plain Ruby method provided by a plugin. Task methods take zero arguments. Here is a trivial example: # .tomo/plugins/foo.rb # This defines a foo:hello task def hello logger.info \"hello, world!\" end The name of the plugin is set automatically based on the name of the .rb file, which in this case is \u201cfoo\u201d. Any public method defined in foo.rb becomes a tomo task. So the example above defines a foo:hello task that prints \u201chello, world!\u201d to the console.","title":"What is a task?"},{"location":"tutorials/writing-custom-tasks/#what-can-a-task-do","text":"Behind the scenes, the Ruby methods you define in your plugin are actually methods on a subclass of TaskLibrary . That means you have full access to the TaskLibrary API within your task method, which includes: logger for printing output (as seen in the example above) remote for running scripts on the remote host settings for accessing project configuration paths for convenient access to filesystem paths on the remote host and more\u2026 For example, a simplified, annotated version of the git:clone task that is built into tomo looks like this: def clone # Halt tomo with an error message if the :git_url setting is nil/unspecified require_setting :git_url # Run \"mkdir -p\" on the remote host to create the parent directory # of the :git_repo_path setting remote.mkdir_p(paths.git_repo.dirname) # Run \"git clone ...\" on the remote host to clone the repo into :git_repo_path remote.run(\"git\", \"clone\", \"--mirror\", settings[:git_url], paths.git_repo) end","title":"What can a task do?"},{"location":"tutorials/writing-custom-tasks/#when-are-tasks-run","text":"Tomo does not have hooks and tasks cannot invoke other tasks. That means that tasks only run when explicitly requested by the user. There are three ways to run a task: deploy setup run For deploy and setup, users invoke your task by including it in the deploy or setup list of tasks in .tomo/config.rb . Additionally, any task can be run on-demand from the command line, like this: $ tomo run foo:hello In the command line case, users can optionally pass arguments to a task. These arguments become available to the task via the :run_args setting. For example, the rails:console task supports command line arguments like this: def console # If this task is run like `tomo run -- rails:console --sandbox` # then settings[:run_args] will be [\"--sandbox\"] args = settings[:run_args] remote.chdir(paths.current) do remote.run(\"bundle\", \"exec\", \"rails\", \"console\", *args, attach: true) end end","title":"When are tasks run?"},{"location":"tutorials/writing-custom-tasks/#how-do-tasks-connect-to-remote-hosts","text":"Notice that none of the examples in this tutorial makes any mention of opening/closing connections or specifying hosts or roles. That is because tomo takes care of connecting to remote hosts and automatically decides which tasks should run on which hosts based on project configuration. By the time a task method is invoked, any necessary SSH connection is already established; remote implicitly refers to that connection. In other words, as a tomo task author you only need to be concerned about what remote scripts to run, not where or how they are executed. For a more in-depth explanation of how configuration drives tomo\u2019s behavior, refer to the configuration docs .","title":"How do tasks connect to remote hosts?"},{"location":"tutorials/writing-custom-tasks/#tutorial","text":"Let\u2019s build something using this knowledge of how tomo tasks work.","title":"Tutorial"},{"location":"tutorials/writing-custom-tasks/#objective","text":"Say we have a Rails app that needs to run code \u2013 PeriodicTask.call , for example \u2013 every day at 06:00. We\u2019d like to do this with a cron job and use tomo to install that cron job on the remote host. For troubleshooting purposes it would be nice to view the list of cron jobs with tomo as well. That sounds like two distinct tomo tasks: cron:install to install the cron job cron:show to list the currently installed cron jobs We want cron:install to be run when we initially set up the remote host. In other words, it should run as part of tomo setup . On the other hand, cron:show is a utility that we can use on the CLI when needed.","title":"Objective"},{"location":"tutorials/writing-custom-tasks/#cronshow","text":"We\u2019ll start by building the simpler of the two tasks: cron:show . First, let\u2019s try to run that task: $ tomo run cron:show tomo run v1.0.0 ERROR: cron:show is not a recognized task. To see a list of all available tasks, run tomo tasks. We haven\u2019t written the task yet, so this error makes sense. Let\u2019s build a skeleton of the cron:show task to fix this error. Create a .tomo/plugins/cron.rb task like this: # .tomo/plugins/cron.rb def show logger.info \"Hi\" end And don\u2019t forget to load the plugin in .tomo/config.rb : # .tomo/config.rb plugin \"./plugins/cron.rb\" Now we can try again: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show Hi \u2714 Ran cron:show on deployer@app.example.com Great! To get a list of cron jobs, we need to run crontab -l on the remote host: def show remote.run \"crontab -l\" end One more try: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer ERROR: The following script failed on deployer@app.example.com (exit status 1). crontab -l You can manually re-execute the script via SSH as follows: ssh -o LogLevel\\=ERROR -A -o ConnectTimeout\\=5 -o StrictHostKeyChecking\\=accept-new -o ControlMaster\\=auto -o ControlPath\\=/var/folders/_v/j_5kgc6n1nz5pb7kfkzz3r5c0000gn/T/tomo_ssh_1f061db77f81ae9e -o ControlPersist\\=30s -o PasswordAuthentication\\=no deployer@app.example.com -- crontab\\ -l For more troubleshooting info, run tomo again using the --debug option. no crontab for deployer Uh oh. There are no cron jobs installed yet, so crontab -l exits with an error. By default, tomo assumes that any remote command the exits with an error status is considered fatal. In this case we just want to see the error output from the crontab command and continue without complaint; that\u2019s where the raise_on_error: false option comes into play: def show remote.run \"crontab -l\", raise_on_error: false end Now we\u2019re all good: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l no crontab for deployer \u2714 Ran cron:show on deployer@app.example.com","title":"cron:show"},{"location":"tutorials/writing-custom-tasks/#croninstall","text":"Before we said that we want a cron:install task that runs as part of tomo setup . Let\u2019s start by adding that task to the list of setup tasks in .tomo/config.rb : # .tomo/config.rb setup do # ... other tasks omitted for brevity run \"cron:install\" end If we try to run tomo setup at this point, we\u2019ll get an error as expected: $ tomo setup tomo setup v1.0.0 ERROR: cron:install is not a recognized task. To see a list of all available tasks, run tomo tasks. Did you mean rbenv:install? Cron jobs can be installed by piping a list of cron definitions to crontab - (the - means to read the definitions from stdin). We can take advantage of this to write a simple cron:install task: def install crontab = <<~CRONTAB SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /var/www/my-app/current; bundle exec rails runner PeriodicTask.call > /var/www/my-app/shared/log/periodic-task.log 2>&1 CRONTAB remote.run \"echo #{crontab.shellescape} | crontab -\" end Note that we are using shellescape as part of Ruby\u2019s built-in shellwords library to safely build the script. We can see what this task does without actually affecting the remote host by using --dry-run option: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Looks good! But we if we made it more powerful with some ERB templating?","title":"cron:install"},{"location":"tutorials/writing-custom-tasks/#templates","text":"Tomo offers a convenient way to use ERB templates with it\u2019s built-in merge_template and write methods. We can use merge_template instead of a hard-coding the cron job: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\" end The ERB template has access to all the same APIs as our task methods; that means we can remove the hard-coded paths from our original cron job specification and use tomo\u2019s paths helper. So our ERB template file ( .tomo/templates/crontab.erb ) could look like this: # .tomo/templates/crontab.erb SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd <%= paths.current %>; bundle exec rails runner PeriodicTask.call > <%= paths.shared.join(\"log/periodic-task.log\") %> 2>&1 Let\u2019s check that it still works: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo SHELL\\=/bin/bash' * '0\\ 6\\ \\*\\ \\*\\ \\*\\ .\\ \\$HOME/.bashrc\\;\\ cd\\ /var/www/my-app/current\\;\\ bundle\\ exec\\ rails\\ runner\\ PeriodicTask.call\\ \\>\\ /var/www/my-app/shared/log/periodic-task.log\\ 2\\>\\&1' * ' | crontab - * Simulated cron:install on deployer@app.example.com (dry run) That\u2019s great, but the output is really verbose. Do we really need to see the full contents of the crontab being echoed? What if our template becomes really large? In tomo, you can mute this output using echo: false , but you can also provide an echo string to show instead of the command. We can use this to echo an abbreviated version: def install template_path = File.expand_path(\"../templates/crontab.erb\", __dir__) crontab = merge_template(template_path) remote.run \"echo #{crontab.shellescape} | crontab -\", echo: \"echo [template:.tomo/templates/crontab.erb] | crontab -\" end And then try it: $ tomo run cron:install --dry-run tomo run v1.0.0 * \u2192 Connecting to deployer@app.example.com * \u2022 cron:install * echo [template:.tomo/templates/crontab.erb] | crontab - * Simulated cron:install on deployer@app.example.com (dry run) Ah, much cleaner!","title":"Templates"},{"location":"tutorials/writing-custom-tasks/#the-result","text":"We now have a cron:install task that will automatically run as part of tomo setup , or can be run manually using tomo run cron:install . Let\u2019s try it for real: $ tomo setup ... [snip] ... \u2022 cron:install echo [template:.tomo/templates/crontab.erb] | crontab - \u2714 Performed setup of my-app on deployer@app.example.com And we can see what is installed with our cron:show task: $ tomo run cron:show tomo run v1.0.0 \u2192 Connecting to deployer@app.example.com \u2022 cron:show crontab -l SHELL=/bin/bash 0 6 * * * . $HOME/.bashrc; cd /home/deployer/apps/my-app/current; bundle exec rails runner PeriodicTask.call > /home/deployer/apps/my-app/shared/log/periodic-task.log 2>&1 \u2714 Ran cron:show on deployer@app.example.com","title":"The result"},{"location":"tutorials/writing-custom-tasks/#next-steps","text":"This tutorial introduced you to writing custom tasks in tomo, but there is much more to explore. For next steps, check out these APIs: Tomo::TaskLibrary Tomo::Remote Tomo::Result Tomo::Paths core plugin helpers (additional methods mixed into the Remote API) And for inspiration, look no further than tomo itself, which has several built-in plugins in lib/tomo/plugin .","title":"Next steps"}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index a900e89030e1f6682f6aa4f6b65a924db85b7abd..056987af9911f679727faf2a5a201fd4ed5af6cd 100644 GIT binary patch delta 13 Ucmb=gXP58h;Al9qZX$aH03R0w!T

Deploying Rails From Scratch

-

In this tutorial we will use tomo to deploy a sample Rails project to a virtual private server (VPS). These instructions use DigitalOcean as the hosting provider, but any provider that offers an Ubuntu 20.04 or 22.04 LTS VPS should work in a similar way. Here are the steps involved (step 1 is the only part that is DigitalOcean-specific):

+

In this tutorial we will use tomo to deploy a sample Rails project to a virtual private server (VPS). These instructions use DigitalOcean as the hosting provider, but any provider that offers an Ubuntu LTS VPS should work in a similar way. Here are the steps involved (step 1 is the only part that is DigitalOcean-specific):

  1. Create an Ubuntu VPS
  2. Install necessary apt packages
  3. @@ -176,7 +176,7 @@

    Deploying Rails From Scratch

    This is a basic tutorial that skips over DNS, TLS, load balancing, PostgreSQL, etc. If you have suggestions for expanding this guide, consider opening an issue or pull request on GitHub. Thanks for reading!

    Create an Ubuntu VPS

    Log into DigitalOcean and create a “Droplet” (aka a VPS). If this is your first time using DigitalOcean, check out their Droplet QuickStart guide for an introduction to the service.

    -

    When creating the Droplet, make sure to choose Ubuntu 20.04 or 22.04 (LTS) x64:

    +

    When creating the Droplet, make sure to choose Ubuntu 20.04, 22.04, or 24.04 (LTS) x64:

    Ubuntu 20.04 LTS

    And use SSH keys for authentication (tomo does not work with password authentication):

    SSH Authentication