tomo 1.1.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5b5ce43b616e968193b178c5bdcf299a066fff72803f7e5d08e5b2f3031da36
4
- data.tar.gz: df6827b5fccc78461849a2aa226f5fc625bc8d5aa3dece49dcd029451e27bc27
3
+ metadata.gz: b4cb608a21d6ce3191225519a3b467a226f0e0b4d1675f08bbed82fd6379696b
4
+ data.tar.gz: 0cb9a00e0eb50501c727d4e58db8b0c5cc686b7e27714ec7bdc11b95387f977d
5
5
  SHA512:
6
- metadata.gz: 71978c54069ac069386d7eac411fe85548492c8bc7f58dbbb48fed6791070c6abad2035d664111c93a827a1b8b03774cdfa1bf14fbd30c370066cee83d630bfb
7
- data.tar.gz: f96b68f1c8f6457548c1d28276a3c599a99c02d4075cdb894d8ec16a9bc06b9f540758db3f7752ee3d5d964120db363145b8f2e0e1718c77610b36e0c386c02e
6
+ metadata.gz: 875563989d848c0dcec9d1b18110712d91603fa734d1aa6fbb5dad454a0911808543f0c0ae9da9510732625d36bc94a0ee5d64d136192e5b4df77b9e130b2292
7
+ data.tar.gz: 936d56fefb5c964b9e19d92ec328cf148de894da697e88d80146428abfefb307551820d9aaba1967d539a2ec22e8f465bf9296bdc364093de9e750e4734bbc05
data/README.md CHANGED
@@ -1,26 +1,26 @@
1
1
  # Tomo
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/tomo.svg)](https://rubygems.org/gems/tomo)
4
- [![Travis](https://img.shields.io/travis/mattbrictson/tomo.svg?label=travis)](https://travis-ci.org/mattbrictson/tomo)
5
- [![Circle](https://circleci.com/gh/mattbrictson/tomo.svg?style=shield)](https://app.circleci.com/pipelines/github/mattbrictson/tomo?branch=master)
4
+ [![Travis](https://img.shields.io/travis/com/mattbrictson/tomo.svg?label=travis)](https://travis-ci.com/github/mattbrictson/tomo)
5
+ [![Circle](https://circleci.com/gh/mattbrictson/tomo/tree/main.svg?style=shield)](https://app.circleci.com/pipelines/github/mattbrictson/tomo?branch=main)
6
6
  [![Code Climate](https://codeclimate.com/github/mattbrictson/tomo/badges/gpa.svg)](https://codeclimate.com/github/mattbrictson/tomo)
7
7
 
8
- Tomo is a friendly command-line tool for deploying Rails apps. It is a new alternative to Capistrano, Mina, and Shipit that aims for simplicity and developer happiness.
8
+ Tomo is a friendly command-line tool for deploying Rails apps.
9
9
 
10
10
  💻 Rich command-line interface with built-in bash completions<br/>
11
11
  ☁️ Multi-environment and role-based multi-host support<br/>
12
12
  💎 Everything you need to deploy a basic Rails app out of the box<br/>
13
13
  🔌 Easily extensible for polyglot projects (not just Rails!)<br/>
14
- 💡 Concise, helpful error messages<br/>
15
14
  📚 Quality documentation<br/>
16
15
  🔬 Minimal dependencies<br/>
17
16
 
18
- See [how tomo compares](https://tomo-deploy.com/comparisons/) to other Ruby deployment tools.
17
+ [→ See how tomo compares to other Ruby deployment tools like Capistrano and Mina.](https://tomo-deploy.com/comparisons/)
19
18
 
20
19
  ---
21
20
 
22
21
  - [Quick start](#quick-start)
23
22
  - [Usage](#usage)
23
+ - [Extending tomo](#extending-tomo)
24
24
  - [Tutorials](#tutorials)
25
25
  - [Reference documentation](#reference-documentation)
26
26
  - [FAQ](#faq)
@@ -31,21 +31,19 @@ See [how tomo compares](https://tomo-deploy.com/comparisons/) to other Ruby depl
31
31
 
32
32
  ## Quick start
33
33
 
34
+ #### Installation
35
+
34
36
  Tomo is distributed as a ruby gem. To install:
35
37
 
36
38
  ```
37
39
  $ gem install tomo
38
40
  ```
39
41
 
40
- For instructions on setting up bash completions, run:
41
-
42
- ```
43
- $ tomo completion-script
44
- ```
42
+ > 💡 **Protip:** run `tomo completion-script` for instructions on setting up bash completions.
45
43
 
46
44
  #### Configuring a project
47
45
 
48
- Tomo is configured via a `.tomo/config.rb` file in your project. To get started, you can use `tomo init` to generate a configuration that works for a basic Rails app.
46
+ 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.
49
47
 
50
48
  ![$ tomo init](./readme_images/tomo-init.png)
51
49
 
@@ -64,7 +62,7 @@ host "user@hostname.or.ip.address"
64
62
  set application: "my-rails-app"
65
63
  set deploy_to: "/var/www/%{application}"
66
64
  set git_url: "git@github.com:my-username/my-rails-app.git"
67
- set git_branch: "master"
65
+ set git_branch: "main"
68
66
  # ...
69
67
 
70
68
  setup do
@@ -87,20 +85,25 @@ deploy do
87
85
  end
88
86
  ```
89
87
 
90
- Check out the [configuration docs](https://tomo-deploy.com/configuration/) for a complete reference.
88
+ #### Next steps
89
+
90
+ [→ The reference docs have a complete guide to tomo configuration.](https://tomo-deploy.com/configuration/)<br>
91
+ [→ Check out the **Deploying Rails From Scratch** tutorial for a step-by-step guide to using tomo with a real app.](https://tomo-deploy.com/tutorials/deploying-rails-from-scratch/)
91
92
 
92
93
  ## Usage
93
94
 
94
- Tomo gives you easy-to-use commands for three common use cases:
95
+ Once your project is configured, you can:
96
+
97
+ 1. Run `tomo setup` to prepare the remote host for its first deploy.
98
+ 2. Run `tomo deploy` to deploy your app.
99
+ 3. Use `tomo run` to invoke one-off tasks, like launching a Rails console.
95
100
 
96
- 1. `tomo setup` prepares a remote host for its first deploy
97
- 2. `tomo deploy` performs a deployment
98
- 3. `tomo run` lets you invoke one-off tasks
101
+ > 💡 **Protip:** add `-h` or `--help` when running any of these commands to see detailed docs and examples.
99
102
 
100
- ### Setup
103
+ ### `tomo setup`
101
104
 
102
105
  `tomo setup` prepares the remote host for its first deploy by sequentially running the
103
- [setup](https://tomo-deploy.com/configuration#setupblock) 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.
106
+ `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.
104
107
 
105
108
  Out of the box, tomo will:
106
109
 
@@ -109,9 +112,12 @@ Out of the box, tomo will:
109
112
  - Create all necessary deployment directories
110
113
  - Create the Rails database, load the schema, and insert seed data
111
114
 
112
- ### Deploy
115
+ [→ Here is the default list of tasks invoked by the setup command.](https://tomo-deploy.com/configuration#setupblock)<br>
116
+ [→ The `tomo setup` section of the reference docs explains supported command-line options.](https://tomo-deploy.com/commands/setup/)
113
117
 
114
- 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](https://tomo-deploy.com/configuration#deployblock) 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:
118
+ ### `tomo deploy`
119
+
120
+ 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:
115
121
 
116
122
  1. Create a release (using the [git:create_release](https://tomo-deploy.com/plugins/git#gitcreate_release) task)
117
123
  2. Build the project (e.g. [bundler:install](https://tomo-deploy.com/plugins/bundler#bundlerinstall), [rails:assets_precompile](https://tomo-deploy.com/plugins/rails#railsassets_precompile))
@@ -120,7 +126,13 @@ Whereas `tomo setup` is typically run once, you can use `tomo deploy` every time
120
126
  5. Restart the app to use the new current release (e.g. [puma:restart](https://tomo-deploy.com/plugins/puma#pumarestart))
121
127
  6. Perform any cleanup (e.g. [bundler:clean](https://tomo-deploy.com/plugins/bundler#bundlerclean))
122
128
 
123
- ### Run
129
+ > 💡 **Protip:** you can abbreviate tomo commands, like `tomo d` for `tomo deploy` or `tomo s` for `tomo setup`.
130
+
131
+ [→ Here is the default list of tasks invoked by the deploy command.](https://tomo-deploy.com/configuration#deployblock)<br>
132
+ [→ The `tomo deploy` section of the reference docs explains supported command-line options, like `--dry-run`.](https://tomo-deploy.com/commands/deploy/)
133
+
134
+
135
+ ### `tomo run [TASK]`
124
136
 
125
137
  Tomo can also `run` individual remote tasks on demand. You can use the `tasks` command to see the list of tasks tomo knows about.
126
138
 
@@ -130,9 +142,15 @@ One of the built-in Rails tasks is `rails:console`, which brings up a fully-inte
130
142
 
131
143
  ![$ tomo run rails:console](./readme_images/tomo-run-rails-console.png)
132
144
 
133
- ### Extending tomo
145
+ > 💡 **Protip:** you can shorten this as `tomo rails:console` (the `run` command is implied).
146
+
147
+ [→ The `tomo run` section of the reference docs explains supported command-line options and has more examples.](https://tomo-deploy.com/commands/run/)
148
+
134
149
 
135
- Tomo has many plugins built-in, but you can easily add your own to extend tomo with custom tasks. By convention, custom plugins are stored in `.tomo/plugins/`. These plugins can define tasks as plain ruby methods. For example:
150
+
151
+ ## Extending tomo
152
+
153
+ Tomo has a powerful plugin system that lets you extend tomo by installing Ruby gems (e.g. [tomo-plugin-sidekiq](https://github.com/mattbrictson/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:
136
154
 
137
155
  ```ruby
138
156
  # .tomo/plugins/my-plugin.rb
@@ -142,8 +160,6 @@ def hello
142
160
  end
143
161
  ```
144
162
 
145
- Use `remote.run` to execute shell scripts on the remote host, similar to how you would use Ruby's `system`. Project settings are accessible via `settings`, which is a plain Ruby hash.
146
-
147
163
  Load your plugin in `config.rb` like this:
148
164
 
149
165
  ```ruby
@@ -156,7 +172,9 @@ And run it!
156
172
 
157
173
  ![$ tomo run my-plugin:hello](./readme_images/tomo-run-hello.png)
158
174
 
159
- Read the [Writing Custom Tasks](https://tomo-deploy.com/tutorials/writing-custom-tasks/) tutorial for an in-depth guide to extending tomo.
175
+ [→ The **Writing Custom Tasks** tutorial has an in-depth explanation of how plugins work.](https://tomo-deploy.com/tutorials/writing-custom-tasks/)<br>
176
+ [→ The **TaskLibrary** API is tomo's DSL for building tasks.](https://tomo-deploy.com/api/TaskLibrary/)<br>
177
+ [→ The **Publishing a Plugin** tutorial explains how to package your plugin as a Ruby gem to share it with the community.](https://tomo-deploy.com/tutorials/publishing-a-plugin/)
160
178
 
161
179
  ## Tutorials
162
180
 
@@ -225,7 +243,7 @@ Next run `tomo setup` for _both_ apps; this will set everything up for both user
225
243
 
226
244
  ## Support
227
245
 
228
- This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you'd like to help by submitting a pull request, or if you've discovered a bug that needs my attention, please let me know. Check out [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/master/CONTRIBUTING.md) to get started. Happy hacking! —Matt
246
+ This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you'd like to help by submitting a pull request, or if you've discovered a bug that needs my attention, please let me know. Check out [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/main/CONTRIBUTING.md) to get started. Happy hacking! —Matt
229
247
 
230
248
  ## License
231
249
 
@@ -233,8 +251,8 @@ The gem is available as open source under the terms of the [MIT License](https:/
233
251
 
234
252
  ## Code of conduct
235
253
 
236
- Everyone interacting in the Tomo project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/mattbrictson/tomo/blob/master/CODE_OF_CONDUCT.md).
254
+ Everyone interacting in the Tomo project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/mattbrictson/tomo/blob/main/CODE_OF_CONDUCT.md).
237
255
 
238
256
  ## Contribution guide
239
257
 
240
- Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/master/CONTRIBUTING.md) guidelines before you dive in.
258
+ Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/main/CONTRIBUTING.md) guidelines before you dive in.
@@ -20,7 +20,7 @@ module Tomo
20
20
  class << self
21
21
  attr_accessor :show_backtrace
22
22
 
23
- def exit(status=true)
23
+ def exit(status=true) # rubocop:disable Style/OptionalBooleanParameter
24
24
  Process.exit(status)
25
25
  end
26
26
  end
@@ -55,7 +55,7 @@ module Tomo
55
55
  argv << "" if argv.shift == "--complete"
56
56
  end
57
57
 
58
- def lookup_command(argv) # rubocop:disable Metrics/CyclomaticComplexity
58
+ def lookup_command(argv)
59
59
  command_name = argv.first unless Completions.active? && argv.length == 1
60
60
  command_name = Abbrev.abbrev(COMMANDS.keys)[command_name]
61
61
  argv.shift if command_name
@@ -1,6 +1,6 @@
1
1
  class Tomo::CLI::Rules
2
2
  class Argument
3
- def initialize(label, multiple: false, required: false, values_proc:)
3
+ def initialize(label, values_proc:, multiple: false, required: false)
4
4
  @label = label
5
5
  @multiple = multiple
6
6
  @required = required
@@ -15,7 +15,7 @@ class Tomo::CLI::Rules
15
15
  state.parsed_arg(arg)
16
16
  end
17
17
 
18
- def candidates(literal: false, state:)
18
+ def candidates(state:, literal: false)
19
19
  values(state).reject { |val| literal && val.start_with?("-") }
20
20
  end
21
21
 
@@ -1,6 +1,6 @@
1
1
  class Tomo::CLI::Rules
2
2
  class Switch
3
- def initialize(key, *switches, required: false, callback_proc:, &convert_proc)
3
+ def initialize(key, *switches, callback_proc:, required: false, &convert_proc)
4
4
  @key = key
5
5
  @switches = switches
6
6
  @callback_proc = callback_proc
@@ -28,7 +28,7 @@ class Tomo::CLI::Rules
28
28
  state.parsed_option(key, value)
29
29
  end
30
30
 
31
- def candidates(switch=nil, literal: false, state:)
31
+ def candidates(switch=nil, state:, literal: false)
32
32
  return [] if literal
33
33
 
34
34
  vals = values(state)
@@ -70,6 +70,14 @@ module Tomo
70
70
  nil
71
71
  end
72
72
 
73
+ def git_branch
74
+ return unless File.file?(".git/config")
75
+
76
+ `git rev-parse --abbrev-ref HEAD`.chomp
77
+ rescue SystemCallError
78
+ nil
79
+ end
80
+
73
81
  def node_version
74
82
  `node --version`.chomp.sub(/^v/i, "")
75
83
  rescue SystemCallError
@@ -82,10 +90,29 @@ module Tomo
82
90
  nil
83
91
  end
84
92
 
93
+ def rubocop?
94
+ File.exist?(".rubocop.yml")
95
+ end
96
+
97
+ def erb_2_2_or_later?
98
+ erb_version = Gem::Version.new(ERB.version[/\d[\d.]+/])
99
+ Gem::Requirement.new(">= 2.2").satisfied_by?(erb_version)
100
+ end
101
+
102
+ def ruby_version_file?
103
+ File.exist?(".ruby-version")
104
+ end
105
+
85
106
  def config_rb_template(app)
86
107
  path = File.expand_path("../templates/config.rb.erb", __dir__)
87
108
  template = IO.read(path)
88
- ERB.new(template).result(binding)
109
+
110
+ # TODO: remove once we drop Ruby 2.5 support?
111
+ if erb_2_2_or_later?
112
+ ERB.new(template, trim_mode: "-").result(binding)
113
+ else
114
+ ERB.new(template, nil, "-").result(binding)
115
+ end
89
116
  end
90
117
  end
91
118
  end
@@ -90,7 +90,7 @@ module Tomo
90
90
  plugins_registry = PluginsRegistry.new
91
91
 
92
92
  (["core"] + plugins.uniq).each do |plug|
93
- if %w[. /].include?(plug[0])
93
+ if plug.start_with?(".", "/")
94
94
  plug = File.expand_path(plug, File.dirname(path)) unless path.nil?
95
95
  plugins_registry.load_plugin_from_path(plug)
96
96
  else
@@ -50,7 +50,7 @@ module Tomo
50
50
  HINT
51
51
  end
52
52
 
53
- def highlighted_lines
53
+ def highlighted_lines # rubocop:disable Metrics/AbcSize
54
54
  first = [1, error_line_no - 1].max
55
55
  last = [dsl_lines.length, error_line_no + 1].min
56
56
  width = last.to_s.length
@@ -39,7 +39,7 @@ module Tomo
39
39
  def constantize(path)
40
40
  parts = path.split("/")
41
41
  parts.reduce(Object) do |parent, part|
42
- child = part.gsub(/^[a-z]|_[a-z]/) { |str| str.chars.last.upcase }
42
+ child = part.gsub(/^[a-z]|_[a-z]/) { |str| str[-1].upcase }
43
43
  parent.const_get(child, false)
44
44
  end
45
45
  end
@@ -55,7 +55,7 @@ module Tomo
55
55
 
56
56
  def scan_for_plugins
57
57
  Gem.find_latest_files("#{PLUGIN_PREFIX}/*.rb").map do |file|
58
- file[%r{#{PLUGIN_PREFIX}/(.+).rb$}, 1].tr("/", "-")
58
+ file[%r{#{PLUGIN_PREFIX}/(.+).rb$}o, 1].tr("/", "-")
59
59
  end.uniq.sort
60
60
  end
61
61
  end
@@ -12,9 +12,10 @@ module Tomo
12
12
  def_delegators :@instance, :interactive?, :prompt, :menu
13
13
  end
14
14
 
15
- def initialize(env=ENV, input=$stdin)
15
+ def initialize(env=ENV, input=$stdin, output=$stdout)
16
16
  @env = env
17
17
  @input = input
18
+ @output = output
18
19
  end
19
20
 
20
21
  def interactive?
@@ -24,7 +25,7 @@ module Tomo
24
25
  def prompt(question)
25
26
  assert_interactive
26
27
 
27
- print question
28
+ output.print question
28
29
  line = input.gets
29
30
  raise_non_interactive if line.nil?
30
31
 
@@ -39,7 +40,7 @@ module Tomo
39
40
 
40
41
  private
41
42
 
42
- attr_reader :env, :input
43
+ attr_reader :env, :input, :output
43
44
 
44
45
  CI_VARS = %w[
45
46
  JENKINS_HOME
@@ -20,7 +20,7 @@ module Tomo
20
20
  path(:"#{method}_path")
21
21
  end
22
22
 
23
- def respond_to_missing?(method, include_private=false)
23
+ def respond_to_missing?(method, include_private)
24
24
  setting?(method) || super
25
25
  end
26
26
 
@@ -56,7 +56,7 @@ module Tomo::Plugin::Bundler
56
56
  raise_on_error: false
57
57
  )
58
58
  version = lockfile_tail[/BUNDLED WITH\n (\S+)$/, 1]
59
- return version if version
59
+ return version if version || dry_run?
60
60
 
61
61
  die <<~REASON
62
62
  Could not guess bundler version from Gemfile.lock.
@@ -12,7 +12,7 @@ module Tomo::Plugin::Core
12
12
  result.success?
13
13
  end
14
14
 
15
- def write(text: nil, template: nil, to:, append: false, **run_opts)
15
+ def write(to:, text: nil, template: nil, append: false, **run_opts)
16
16
  assert_text_or_template_required!(text, template)
17
17
  text = merge_template(template) unless template.nil?
18
18
  message = "Writing #{text.bytesize} bytes to #{to}"
@@ -41,7 +41,7 @@ module Tomo::Plugin::Core
41
41
  current = read_current_release
42
42
 
43
43
  remote.chdir(paths.releases) do
44
- releases = remote.list_files.grep(/^#{RELEASE_REGEXP}$/).sort
44
+ releases = remote.list_files.grep(/^#{RELEASE_REGEXP}$/o).sort
45
45
  desired_count -= 1 if releases.delete(current)
46
46
  return if releases.length <= desired_count
47
47
 
@@ -122,7 +122,7 @@ module Tomo::Plugin::Core
122
122
  result = remote.run("readlink", paths.current, raise_on_error: false, silent: true)
123
123
  return nil if result.failure?
124
124
 
125
- result.stdout.strip[%r{/(#{RELEASE_REGEXP})$}, 1]
125
+ result.stdout.strip[%r{/(#{RELEASE_REGEXP})$}o, 1]
126
126
  end
127
127
  end
128
128
  end
@@ -1,7 +1,7 @@
1
1
  require "monitor"
2
2
 
3
3
  module Tomo::Plugin::Env
4
- class Tasks < Tomo::TaskLibrary
4
+ class Tasks < Tomo::TaskLibrary # rubocop:disable Metrics/ClassLength
5
5
  include MonitorMixin
6
6
 
7
7
  def show
@@ -10,8 +10,8 @@ module Tomo::Plugin::Env
10
10
  end
11
11
 
12
12
  def setup
13
- update
14
13
  modify_bashrc
14
+ update
15
15
  end
16
16
 
17
17
  def update
@@ -103,12 +103,37 @@ module Tomo::Plugin::Env
103
103
  existing_rc = remote.capture("cat", paths.bashrc, raise_on_error: false)
104
104
  return if existing_rc.include?(". #{env_path}")
105
105
 
106
+ fail_if_different_app_already_configured!(existing_rc)
107
+
106
108
  remote.write(text: <<~BASHRC + existing_rc, to: paths.bashrc)
107
- if [ -f #{env_path} ]; then
108
- . #{env_path}
109
- fi
109
+ if [ -f #{env_path} ]; then # DO NOT MODIFY THESE LINES
110
+ . #{env_path} # ENV MAINTAINED BY TOMO
111
+ fi #{' ' * env_path.to_s.length}# END TOMO ENV
110
112
 
111
113
  BASHRC
112
114
  end
115
+
116
+ def fail_if_different_app_already_configured!(bashrc)
117
+ existing_env_path = bashrc[/\s*\.\s+(.+)\s+# ENV MAINTAINED BY TOMO/, 1]
118
+ return if existing_env_path.nil?
119
+
120
+ die <<~REASON
121
+ Based on the contents of #{paths.bashrc}, it looks like another application
122
+ is already being deployed via tomo to this host, using the following envrc
123
+ path:
124
+
125
+ #{existing_env_path}
126
+
127
+ Tomo is designed such that only one application can be deployed to a given
128
+ user@host. To deploy multiple applications to the same host, use a separate
129
+ deployer user per app. Refer to the tomo FAQ for details:
130
+
131
+ https://tomo-deploy.com/#faq
132
+
133
+ You may be receiving this message in error if you recently renamed or
134
+ reconfigured your application. In this case, remove the references to the
135
+ old envrc path in the host's #{paths.bashrc} and re-run env:setup.
136
+ REASON
137
+ end
113
138
  end
114
139
  end
@@ -7,7 +7,7 @@ module Tomo::Plugin
7
7
 
8
8
  helpers Tomo::Plugin::Git::Helpers
9
9
  tasks Tomo::Plugin::Git::Tasks
10
- defaults git_branch: "master",
10
+ defaults git_branch: nil,
11
11
  git_repo_path: "%{deploy_to}/git_repo",
12
12
  git_exclusions: [],
13
13
  git_env: { GIT_SSH_COMMAND: "ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no" },
@@ -8,6 +8,10 @@ module Tomo::Plugin::Rails
8
8
  remote.rails("console", settings[:run_args], attach: true)
9
9
  end
10
10
 
11
+ def db_console
12
+ remote.rails("dbconsole", "--include-password", settings[:run_args], attach: true)
13
+ end
14
+
11
15
  def db_migrate
12
16
  remote.rake("db:migrate")
13
17
  end
@@ -31,8 +31,7 @@ module Tomo::Plugin::Rbenv
31
31
  end
32
32
 
33
33
  def compile_ruby
34
- require_setting :rbenv_ruby_version
35
- ruby_version = settings[:rbenv_ruby_version]
34
+ ruby_version = version_setting || extract_ruby_ver_from_version_file
36
35
 
37
36
  unless ruby_installed?(ruby_version)
38
37
  logger.info(
@@ -51,5 +50,22 @@ module Tomo::Plugin::Rbenv
51
50
  end
52
51
  false
53
52
  end
53
+
54
+ def version_setting
55
+ settings[:rbenv_ruby_version]
56
+ end
57
+
58
+ def extract_ruby_ver_from_version_file
59
+ path = paths.release.join(".ruby-version")
60
+ version = remote.capture("cat", path, raise_on_error: false).strip
61
+ return version unless version.empty?
62
+
63
+ return RUBY_VERSION if dry_run?
64
+
65
+ die <<~REASON
66
+ Could not guess ruby version from .ruby-version file.
67
+ Use the :rbenv_ruby_version setting to specify the version of ruby to install.
68
+ REASON
69
+ end
54
70
  end
55
71
  end
@@ -7,7 +7,7 @@ module Tomo
7
7
  @concurrency = concurrency
8
8
  end
9
9
 
10
- def to_s # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
10
+ def to_s # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
11
11
  desc = []
12
12
  threads = [applicable_hosts.length, concurrency].min
13
13
  desc << "CONCURRENTLY (#{threads} THREADS):" if threads > 1
@@ -82,7 +82,7 @@ module Tomo
82
82
  end
83
83
 
84
84
  def export_env
85
- exports = @env.reject { |_, value| value.nil? }
85
+ exports = @env.compact
86
86
  return if exports.empty?
87
87
 
88
88
  [
@@ -26,7 +26,7 @@ module Tomo
26
26
  end
27
27
 
28
28
  def ssh_subprocess(script, verbose: false)
29
- ssh_args = build_args(script, verbose)
29
+ ssh_args = build_args(script, verbose: verbose)
30
30
  handle_data = ->(data) { logger.script_output(script, data) }
31
31
 
32
32
  logger.script_start(script)
@@ -50,7 +50,7 @@ module Tomo
50
50
  Tomo.logger
51
51
  end
52
52
 
53
- def build_args(script, verbose=false)
53
+ def build_args(script, verbose: false)
54
54
  options.build_args(host, script, control_path, verbose)
55
55
  end
56
56
 
@@ -15,10 +15,10 @@ module Tomo
15
15
 
16
16
  def assert_valid_executable!
17
17
  result = begin
18
- ChildProcess.execute(executable, "-V")
19
- rescue StandardError => e
20
- handle_bad_executable(e)
21
- end
18
+ ChildProcess.execute(executable, "-V")
19
+ rescue StandardError => e
20
+ handle_bad_executable(e)
21
+ end
22
22
 
23
23
  Tomo.logger.debug(result.output)
24
24
  return if result.success? && supported?(result.output)
@@ -1,3 +1,6 @@
1
+ <% if rubocop? -%>
2
+ # rubocop:disable Style/FormatStringToken
3
+ <% end -%>
1
4
  plugin "git"
2
5
  plugin "env"
3
6
  plugin "bundler"
@@ -11,11 +14,13 @@ host "user@hostname.or.ip.address"
11
14
 
12
15
  set application: <%= app.inspect %>
13
16
  set deploy_to: "/var/www/%{application}"
17
+ <% unless ruby_version_file? -%>
14
18
  set rbenv_ruby_version: <%= RUBY_VERSION.inspect %>
19
+ <% end -%>
15
20
  set nodenv_node_version: <%= node_version&.inspect || "nil # FIXME" %>
16
21
  set nodenv_yarn_version: <%= yarn_version.inspect %>
17
22
  set git_url: <%= git_origin_url&.inspect || "nil # FIXME" %>
18
- set git_branch: "master"
23
+ set git_branch: <%= git_branch&.inspect || "nil # FIXME" %>
19
24
  set git_exclusions: %w[
20
25
  .tomo/
21
26
  spec/
@@ -72,3 +77,6 @@ deploy do
72
77
  run "bundler:clean"
73
78
  run "core:log_revision"
74
79
  end
80
+ <% if rubocop? -%>
81
+ # rubocop:enable Style/FormatStringToken
82
+ <% end -%>
@@ -1,7 +1,7 @@
1
1
  module Tomo
2
2
  module Testing
3
3
  module CLIExtensions
4
- def exit(status=true)
4
+ def exit(status=true) # rubocop:disable Style/OptionalBooleanParameter
5
5
  raise MockedExitError, status
6
6
  end
7
7
  end
@@ -80,9 +80,9 @@ module Tomo
80
80
  end
81
81
 
82
82
  def build_image
83
- Local.capture(
84
- "docker build #{build_dir}"
85
- )[/Successfully built (\S+)$/i, 1]
83
+ tag = "tomo_testing:latest"
84
+ Local.capture("docker build --tag #{tag} #{build_dir}")
85
+ tag
86
86
  end
87
87
 
88
88
  def start_container
@@ -1,3 +1,3 @@
1
1
  module Tomo
2
- VERSION = "1.1.2".freeze
2
+ VERSION = "1.4.1".freeze
3
3
  end
metadata CHANGED
@@ -1,141 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tomo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Brictson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-13 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.0'
27
- - !ruby/object:Gem::Dependency
28
- name: concurrent-ruby
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.1'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '1.1'
41
- - !ruby/object:Gem::Dependency
42
- name: minitest
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '5.11'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '5.11'
55
- - !ruby/object:Gem::Dependency
56
- name: minitest-ci
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '3.4'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '3.4'
69
- - !ruby/object:Gem::Dependency
70
- name: minitest-reporters
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '1.3'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '1.3'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '13.0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '13.0'
97
- - !ruby/object:Gem::Dependency
98
- name: rubocop
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - '='
102
- - !ruby/object:Gem::Version
103
- version: 0.85.1
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - '='
109
- - !ruby/object:Gem::Version
110
- version: 0.85.1
111
- - !ruby/object:Gem::Dependency
112
- name: rubocop-minitest
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - '='
116
- - !ruby/object:Gem::Version
117
- version: 0.9.0
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - '='
123
- - !ruby/object:Gem::Version
124
- version: 0.9.0
125
- - !ruby/object:Gem::Dependency
126
- name: rubocop-performance
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - '='
130
- - !ruby/object:Gem::Version
131
- version: 1.6.1
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - '='
137
- - !ruby/object:Gem::Version
138
- version: 1.6.1
11
+ date: 2020-11-26 00:00:00.000000000 Z
12
+ dependencies: []
139
13
  description: Tomo is a feature-rich deployment tool that contains everything you need
140
14
  to deploy a basic Rails app out of the box. It has an opinionated, production-tested
141
15
  set of defaults, but is easily extensible via a well-documented plugin system. Unlike
@@ -191,7 +65,6 @@ files:
191
65
  - lib/tomo/configuration/environment.rb
192
66
  - lib/tomo/configuration/glob.rb
193
67
  - lib/tomo/configuration/plugin_file_not_found_error.rb
194
- - lib/tomo/configuration/plugin_resolver.rb
195
68
  - lib/tomo/configuration/plugins_registry.rb
196
69
  - lib/tomo/configuration/plugins_registry/file_resolver.rb
197
70
  - lib/tomo/configuration/plugins_registry/gem_resolver.rb
@@ -298,7 +171,7 @@ metadata:
298
171
  bug_tracker_uri: https://github.com/mattbrictson/tomo/issues
299
172
  changelog_uri: https://github.com/mattbrictson/tomo/releases
300
173
  source_code_uri: https://github.com/mattbrictson/tomo
301
- homepage_uri: https://tomo-deploy.com/
174
+ homepage_uri: https://github.com/mattbrictson/tomo
302
175
  documentation_uri: https://tomo-deploy.com/
303
176
  post_install_message:
304
177
  rdoc_options: []
@@ -1,63 +0,0 @@
1
- module Tomo
2
- class Configuration
3
- class PluginResolver
4
- PLUGIN_PREFIX = "tomo/plugin".freeze
5
- private_constant :PLUGIN_PREFIX
6
-
7
- def self.resolve(name)
8
- new(name).plugin_module
9
- end
10
-
11
- def initialize(name)
12
- @name = name
13
- end
14
-
15
- def plugin_module
16
- plugin_path = [PLUGIN_PREFIX, name.tr("-", "/")].join("/")
17
- require plugin_path
18
-
19
- plugin = constantize(plugin_path)
20
- assert_compatible_api(plugin)
21
-
22
- plugin
23
- rescue LoadError => e
24
- raise unless e.message.match?(/\s#{Regexp.quote(plugin_path)}$/)
25
-
26
- raise_unknown_plugin_error(e)
27
- end
28
-
29
- private
30
-
31
- attr_reader :name
32
-
33
- def assert_compatible_api(plugin)
34
- return if plugin.is_a?(::Tomo::PluginDSL)
35
-
36
- raise "#{plugin} does not extend Tomo::PluginDSL"
37
- end
38
-
39
- def constantize(path)
40
- parts = path.split("/")
41
- parts.reduce(Object) do |parent, part|
42
- child = part.gsub(/^[a-z]|_[a-z]/) { |str| str.chars.last.upcase }
43
- parent.const_get(child, false)
44
- end
45
- end
46
-
47
- def raise_unknown_plugin_error(error)
48
- UnknownPluginError.raise_with(
49
- error.message,
50
- name: name,
51
- gem_name: "#{PLUGIN_PREFIX}/#{name}".tr("/", "-"),
52
- known_plugins: scan_for_plugins
53
- )
54
- end
55
-
56
- def scan_for_plugins
57
- Gem.find_latest_files("#{PLUGIN_PREFIX}/*.rb").map do |file|
58
- file[%r{#{PLUGIN_PREFIX}/(.+).rb$}, 1].tr("/", "-")
59
- end.uniq.sort
60
- end
61
- end
62
- end
63
- end