tomo 0.18.0 → 1.1.2

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/lib/tomo/cli.rb +1 -3
  4. data/lib/tomo/cli/common_options.rb +4 -12
  5. data/lib/tomo/cli/deploy_options.rb +2 -7
  6. data/lib/tomo/cli/parser.rb +1 -6
  7. data/lib/tomo/cli/project_options.rb +1 -3
  8. data/lib/tomo/cli/rules.rb +4 -22
  9. data/lib/tomo/cli/rules/switch.rb +1 -5
  10. data/lib/tomo/cli/rules/value_switch.rb +1 -2
  11. data/lib/tomo/cli/rules_evaluator.rb +3 -13
  12. data/lib/tomo/cli/usage.rb +1 -3
  13. data/lib/tomo/commands/default.rb +1 -5
  14. data/lib/tomo/commands/run.rb +1 -3
  15. data/lib/tomo/configuration.rb +5 -11
  16. data/lib/tomo/configuration/dsl/error_formatter.rb +0 -4
  17. data/lib/tomo/configuration/dsl/hosts_and_settings.rb +1 -2
  18. data/lib/tomo/configuration/plugins_registry.rb +1 -2
  19. data/lib/tomo/configuration/unknown_environment_error.rb +1 -4
  20. data/lib/tomo/console.rb +2 -8
  21. data/lib/tomo/console/menu.rb +1 -2
  22. data/lib/tomo/host.rb +1 -2
  23. data/lib/tomo/plugin/bundler/tasks.rb +8 -8
  24. data/lib/tomo/plugin/core/tasks.rb +3 -16
  25. data/lib/tomo/plugin/env/tasks.rb +2 -7
  26. data/lib/tomo/plugin/git.rb +0 -3
  27. data/lib/tomo/plugin/git/tasks.rb +4 -16
  28. data/lib/tomo/plugin/nodenv/tasks.rb +1 -3
  29. data/lib/tomo/plugin/puma.rb +0 -3
  30. data/lib/tomo/plugin/puma/systemd/service.erb +1 -1
  31. data/lib/tomo/plugin/puma/tasks.rb +6 -15
  32. data/lib/tomo/plugin/rails/helpers.rb +1 -1
  33. data/lib/tomo/plugin/rails/tasks.rb +2 -4
  34. data/lib/tomo/plugin/testing.rb +1 -3
  35. data/lib/tomo/remote.rb +1 -3
  36. data/lib/tomo/runtime.rb +3 -6
  37. data/lib/tomo/runtime/concurrent_ruby_thread_pool.rb +1 -4
  38. data/lib/tomo/runtime/execution_plan.rb +1 -4
  39. data/lib/tomo/runtime/explanation.rb +1 -7
  40. data/lib/tomo/runtime/settings_interpolation.rb +1 -3
  41. data/lib/tomo/runtime/settings_required_error.rb +1 -3
  42. data/lib/tomo/runtime/task_runner.rb +2 -7
  43. data/lib/tomo/runtime/unknown_task_error.rb +1 -4
  44. data/lib/tomo/script.rb +1 -5
  45. data/lib/tomo/shell_builder.rb +5 -10
  46. data/lib/tomo/ssh/child_process.rb +6 -13
  47. data/lib/tomo/ssh/connection.rb +3 -16
  48. data/lib/tomo/ssh/connection_validator.rb +1 -4
  49. data/lib/tomo/ssh/executable_error.rb +1 -2
  50. data/lib/tomo/ssh/options.rb +2 -5
  51. data/lib/tomo/task_api.rb +4 -15
  52. data/lib/tomo/testing.rb +0 -2
  53. data/lib/tomo/testing/Dockerfile +1 -3
  54. data/lib/tomo/testing/connection.rb +1 -6
  55. data/lib/tomo/testing/docker_image.rb +4 -17
  56. data/lib/tomo/testing/local.rb +1 -3
  57. data/lib/tomo/testing/mock_plugin_tester.rb +27 -4
  58. data/lib/tomo/testing/mocked_exit_error.rb +1 -1
  59. data/lib/tomo/testing/ubuntu_setup.sh +1 -2
  60. data/lib/tomo/version.rb +1 -1
  61. metadata +13 -29
  62. data/lib/tomo/testing/docker_plugin_tester.rb +0 -39
  63. data/lib/tomo/testing/plugin_tester.rb +0 -33
@@ -6,7 +6,6 @@ module Tomo::Plugin::Core
6
6
  RELEASE_REGEXP = /\d{14}/.freeze
7
7
  private_constant :RELEASE_REGEXP
8
8
 
9
- # rubocop:disable Metrics/AbcSize
10
9
  def setup_directories
11
10
  dirs = [
12
11
  paths.deploy_to,
@@ -18,7 +17,6 @@ module Tomo::Plugin::Core
18
17
 
19
18
  remote.mkdir_p(*dirs)
20
19
  end
21
- # rubocop:enable Metrics/AbcSize
22
20
 
23
21
  def symlink_shared
24
22
  return if linked_dirs.empty? && linked_files.empty?
@@ -36,8 +34,7 @@ module Tomo::Plugin::Core
36
34
  remote.run "mv", "-fT", tmp_link, paths.current
37
35
  end
38
36
 
39
- # rubocop:disable Metrics/AbcSize
40
- def clean_releases
37
+ def clean_releases # rubocop:disable Metrics/AbcSize
41
38
  desired_count = settings[:keep_releases].to_i
42
39
  return if desired_count < 1
43
40
 
@@ -51,15 +48,13 @@ module Tomo::Plugin::Core
51
48
  remote.rm_rf(*releases.take(releases.length - desired_count))
52
49
  end
53
50
  end
54
- # rubocop:enable Metrics/AbcSize
55
51
 
56
52
  def write_release_json
57
53
  json = JSON.pretty_generate(remote.release)
58
54
  remote.write(text: "#{json}\n", to: paths.release_json)
59
55
  end
60
56
 
61
- # rubocop:disable Metrics/AbcSize
62
- def log_revision
57
+ def log_revision # rubocop:disable Metrics/AbcSize
63
58
  ref = remote.release[:ref]
64
59
  revision = remote.release[:revision]
65
60
 
@@ -71,7 +66,6 @@ module Tomo::Plugin::Core
71
66
 
72
67
  remote.write(text: message, to: paths.revision_log, append: true)
73
68
  end
74
- # rubocop:enable Metrics/AbcSize
75
69
 
76
70
  private
77
71
 
@@ -83,7 +77,6 @@ module Tomo::Plugin::Core
83
77
  settings[:linked_files] || []
84
78
  end
85
79
 
86
- # rubocop:disable Metrics/AbcSize
87
80
  def shared_directories
88
81
  result = linked_dirs.map { |name| paths.shared.join(name) }
89
82
  linked_files.each do |name|
@@ -91,7 +84,6 @@ module Tomo::Plugin::Core
91
84
  end
92
85
  result.map(&:to_s).uniq - [paths.shared.to_s]
93
86
  end
94
- # rubocop:enable Metrics/AbcSize
95
87
 
96
88
  def symlink_shared_files
97
89
  return if linked_files.empty?
@@ -127,12 +119,7 @@ module Tomo::Plugin::Core
127
119
  end
128
120
 
129
121
  def read_current_release
130
- result = remote.run(
131
- "readlink",
132
- paths.current,
133
- raise_on_error: false,
134
- silent: true
135
- )
122
+ result = remote.run("readlink", paths.current, raise_on_error: false, silent: true)
136
123
  return nil if result.failure?
137
124
 
138
125
  result.stdout.strip[%r{/(#{RELEASE_REGEXP})$}, 1]
@@ -14,7 +14,6 @@ module Tomo::Plugin::Env
14
14
  modify_bashrc
15
15
  end
16
16
 
17
- # rubocop:disable Metrics/MethodLength
18
17
  def update
19
18
  return if settings[:env_vars].empty?
20
19
 
@@ -30,7 +29,6 @@ module Tomo::Plugin::Env
30
29
  end
31
30
  end
32
31
  end
33
- # rubocop:enable Metrics/MethodLength
34
32
 
35
33
  def set
36
34
  return if settings[:run_args].empty?
@@ -67,10 +65,7 @@ module Tomo::Plugin::Env
67
65
  end
68
66
 
69
67
  def read_existing
70
- remote.capture(
71
- "cat", paths.env,
72
- raise_on_error: false, echo: false, silent: true
73
- )
68
+ remote.capture("cat", paths.env, raise_on_error: false, echo: false, silent: true)
74
69
  end
75
70
 
76
71
  def replace_entry(text, name, value)
@@ -84,7 +79,7 @@ module Tomo::Plugin::Env
84
79
 
85
80
  def prepend_entry(text, name, value)
86
81
  text.prepend("\n") unless text.start_with?("\n")
87
- text.prepend("export #{name.to_s.shellescape}=#{value.shellescape}")
82
+ text.prepend("export #{name.to_s.shellescape}=#{value.to_s.shellescape}")
88
83
  end
89
84
 
90
85
  def contains_entry?(text, name)
@@ -7,14 +7,11 @@ module Tomo::Plugin
7
7
 
8
8
  helpers Tomo::Plugin::Git::Helpers
9
9
  tasks Tomo::Plugin::Git::Tasks
10
-
11
- # rubocop:disable Layout/LineLength
12
10
  defaults git_branch: "master",
13
11
  git_repo_path: "%{deploy_to}/git_repo",
14
12
  git_exclusions: [],
15
13
  git_env: { GIT_SSH_COMMAND: "ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no" },
16
14
  git_ref: nil,
17
15
  git_url: nil
18
- # rubocop:enable Layout/LineLength
19
16
  end
20
17
  end
@@ -3,7 +3,6 @@ require "time"
3
3
 
4
4
  module Tomo::Plugin::Git
5
5
  class Tasks < Tomo::TaskLibrary
6
- # rubocop:disable Metrics/AbcSize
7
6
  def clone
8
7
  require_setting :git_url
9
8
 
@@ -15,8 +14,7 @@ module Tomo::Plugin::Git
15
14
  end
16
15
  end
17
16
 
18
- # rubocop:disable Metrics/MethodLength
19
- def create_release
17
+ def create_release # rubocop:disable Metrics/AbcSize
20
18
  remote.chdir(paths.git_repo) do
21
19
  remote.git("remote update --prune")
22
20
  end
@@ -32,8 +30,6 @@ module Tomo::Plugin::Git
32
30
  )
33
31
  end
34
32
  end
35
- # rubocop:enable Metrics/MethodLength
36
- # rubocop:enable Metrics/AbcSize
37
33
 
38
34
  private
39
35
 
@@ -66,19 +62,13 @@ module Tomo::Plugin::Git
66
62
  exclusions = settings[:git_exclusions] || []
67
63
  attributes = exclusions.map { |excl| "#{excl} export-ignore" }.join("\n")
68
64
 
69
- remote.write(
70
- text: attributes,
71
- to: paths.git_repo.join("info/attributes")
72
- )
65
+ remote.write(text: attributes, to: paths.git_repo.join("info/attributes"))
73
66
  end
74
67
 
75
- # rubocop:disable Metrics/AbcSize
76
- # rubocop:disable Metrics/MethodLength
77
- def store_release_info
68
+ def store_release_info # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
78
69
  log = remote.chdir(paths.git_repo) do
79
70
  remote.git(
80
- 'log -n1 --date=iso --pretty=format:"%H/%cd/%ae" '\
81
- "#{ref.shellescape} --",
71
+ %Q(log -n1 --date=iso --pretty=format:"%H/%cd/%ae" #{ref.shellescape} --),
82
72
  silent: true
83
73
  ).stdout.strip
84
74
  end
@@ -92,7 +82,5 @@ module Tomo::Plugin::Git
92
82
  remote.release[:deploy_date] = Time.now.to_s
93
83
  remote.release[:deploy_user] = settings.fetch(:local_user)
94
84
  end
95
- # rubocop:enable Metrics/MethodLength
96
- # rubocop:enable Metrics/AbcSize
97
85
  end
98
86
  end
@@ -34,9 +34,7 @@ module Tomo::Plugin::Nodenv
34
34
  require_setting :nodenv_node_version
35
35
  node_version = settings[:nodenv_node_version]
36
36
 
37
- unless node_installed?(node_version)
38
- remote.run "nodenv install #{node_version.shellescape}"
39
- end
37
+ remote.run "nodenv install #{node_version.shellescape}" unless node_installed?(node_version)
40
38
  remote.run "nodenv global #{node_version.shellescape}"
41
39
  end
42
40
 
@@ -5,8 +5,6 @@ module Tomo::Plugin
5
5
  extend Tomo::PluginDSL
6
6
 
7
7
  tasks Tomo::Plugin::Puma::Tasks
8
-
9
- # rubocop:disable Layout/LineLength
10
8
  defaults puma_check_timeout: 15,
11
9
  puma_host: "0.0.0.0",
12
10
  puma_port: "3000",
@@ -16,6 +14,5 @@ module Tomo::Plugin
16
14
  puma_systemd_socket_path: ".config/systemd/user/%{puma_systemd_socket}",
17
15
  puma_systemd_service_template_path: File.expand_path("puma/systemd/service.erb", __dir__),
18
16
  puma_systemd_socket_template_path: File.expand_path("puma/systemd/socket.erb", __dir__)
19
- # rubocop:enable Layout/LineLength
20
17
  end
21
18
  end
@@ -19,4 +19,4 @@ WorkingDirectory=<%= paths.current %>
19
19
  # Environment=PUMA_DEBUG=1
20
20
 
21
21
  [Install]
22
- WantedBy=multi-user.target
22
+ WantedBy=default.target
@@ -2,8 +2,7 @@ module Tomo::Plugin::Puma
2
2
  class Tasks < Tomo::TaskLibrary
3
3
  SystemdUnit = Struct.new(:name, :template, :path)
4
4
 
5
- # rubocop:disable Metrics/AbcSize
6
- def setup_systemd
5
+ def setup_systemd # rubocop:disable Metrics/AbcSize
7
6
  linger_must_be_enabled!
8
7
 
9
8
  setup_directories
@@ -13,7 +12,6 @@ module Tomo::Plugin::Puma
13
12
  remote.run "systemctl --user daemon-reload"
14
13
  remote.run "systemctl", "--user", "enable", service.name, socket.name
15
14
  end
16
- # rubocop:enable Metrics/AbcSize
17
15
 
18
16
  %i[start stop status].each do |action|
19
17
  define_method(action) do
@@ -37,9 +35,7 @@ module Tomo::Plugin::Puma
37
35
  end
38
36
 
39
37
  def log
40
- remote.attach "journalctl", "-q",
41
- raw("--user-unit=#{service.name.shellescape}"),
42
- *settings[:run_args]
38
+ remote.attach "journalctl", "-q", raw("--user-unit=#{service.name.shellescape}"), *settings[:run_args]
43
39
  end
44
40
 
45
41
  private
@@ -102,22 +98,17 @@ module Tomo::Plugin::Puma
102
98
  end
103
99
 
104
100
  def assert_active!
105
- return true if remote.run? "systemctl", "--user", "is-active",
106
- service.name,
107
- silent: true, raise_on_error: false
101
+ return true if remote.run? "systemctl", "--user", "is-active", service.name, silent: true, raise_on_error: false
108
102
 
109
- remote.run "systemctl", "--user", "status", service.name,
110
- raise_on_error: false
111
- remote.run "journalctl -q -n 50 --user-unit=#{service.name.shellescape}",
112
- raise_on_error: false
103
+ remote.run "systemctl", "--user", "status", service.name, raise_on_error: false
104
+ remote.run "journalctl -q -n 50 --user-unit=#{service.name.shellescape}", raise_on_error: false
113
105
 
114
106
  die "puma failed to start (see previous systemctl and journalctl output)"
115
107
  end
116
108
 
117
109
  def listening?
118
110
  test_url = "http://localhost:#{port}"
119
- remote.run? "curl -sS --connect-timeout 1 --max-time 10 #{test_url}"\
120
- " > /dev/null"
111
+ remote.run? "curl -sS --connect-timeout 1 --max-time 10 #{test_url} > /dev/null"
121
112
  end
122
113
  end
123
114
  end
@@ -7,7 +7,7 @@ module Tomo::Plugin::Rails
7
7
  end
8
8
 
9
9
  def rake(*args, **opts)
10
- prepend("exec", "rails") do
10
+ prepend("exec", "rake") do
11
11
  bundle(*args, **opts)
12
12
  end
13
13
  end
@@ -40,11 +40,9 @@ module Tomo::Plugin::Rails
40
40
 
41
41
  def db_structure_load
42
42
  if !structure_sql_present?
43
- logger.warn "db/structure.sql is not present; "\
44
- "skipping db:structure:load."
43
+ logger.warn "db/structure.sql is not present; skipping db:structure:load."
45
44
  elsif database_schema_loaded?
46
- logger.info "Database structure already loaded; "\
47
- "skipping db:structure:load."
45
+ logger.info "Database structure already loaded; skipping db:structure:load."
48
46
  else
49
47
  remote.rake("db:structure:load")
50
48
  end
@@ -1,6 +1,4 @@
1
- unless defined?(Tomo::Testing)
2
- raise "The testing plugin cannot be used outside of unit tests"
3
- end
1
+ raise "The testing plugin cannot be used outside of unit tests" unless defined?(Tomo::Testing)
4
2
 
5
3
  module Tomo::Plugin
6
4
  class Testing < Tomo::TaskLibrary
@@ -21,9 +21,7 @@ module Tomo
21
21
 
22
22
  def attach(*command, default_chdir: nil, **command_opts)
23
23
  full_command = shell_builder.build(*command, default_chdir: default_chdir)
24
- ssh.ssh_exec(
25
- Script.new(full_command, **{ pty: true }.merge(command_opts))
26
- )
24
+ ssh.ssh_exec(Script.new(full_command, **{ pty: true }.merge(command_opts)))
27
25
  end
28
26
 
29
27
  def run(*command, attach: false, default_chdir: nil, **command_opts)
@@ -3,8 +3,7 @@ require "time"
3
3
  module Tomo
4
4
  class Runtime
5
5
  autoload :ConcurrentRubyLoadError, "tomo/runtime/concurrent_ruby_load_error"
6
- autoload :ConcurrentRubyThreadPool,
7
- "tomo/runtime/concurrent_ruby_thread_pool"
6
+ autoload :ConcurrentRubyThreadPool, "tomo/runtime/concurrent_ruby_thread_pool"
8
7
  autoload :Context, "tomo/runtime/context"
9
8
  autoload :Current, "tomo/runtime/current"
10
9
  autoload :ExecutionPlan, "tomo/runtime/execution_plan"
@@ -28,8 +27,7 @@ module Tomo
28
27
 
29
28
  attr_reader :tasks
30
29
 
31
- def initialize(deploy_tasks:, setup_tasks:, hosts:, task_filter:,
32
- settings:, plugins_registry:)
30
+ def initialize(deploy_tasks:, setup_tasks:, hosts:, task_filter:, settings:, plugins_registry:)
33
31
  @deploy_tasks = deploy_tasks.freeze
34
32
  @setup_tasks = setup_tasks.freeze
35
33
  @hosts = hosts.freeze
@@ -68,8 +66,7 @@ module Tomo
68
66
 
69
67
  private
70
68
 
71
- attr_reader :deploy_tasks, :setup_tasks, :hosts, :task_filter, :settings,
72
- :plugins_registry
69
+ attr_reader :deploy_tasks, :setup_tasks, :hosts, :task_filter, :settings, :plugins_registry
73
70
 
74
71
  def new_task_runner(release_type, args)
75
72
  run_settings = { release_path: release_path_for(release_type) }
@@ -4,10 +4,7 @@ begin
4
4
  gem "concurrent-ruby", concurrent_ver
5
5
  require "concurrent"
6
6
  rescue LoadError => e
7
- Tomo::Runtime::ConcurrentRubyLoadError.raise_with(
8
- e.message,
9
- version: concurrent_ver
10
- )
7
+ Tomo::Runtime::ConcurrentRubyLoadError.raise_with(e.message, version: concurrent_ver)
11
8
  end
12
9
 
13
10
  module Tomo
@@ -77,10 +77,7 @@ module Tomo
77
77
  def build_plan(tasks, task_filter)
78
78
  tasks.each_with_object([]) do |task, result|
79
79
  steps = hosts.map do |host|
80
- HostExecutionStep.new(
81
- tasks: task, host: host,
82
- task_filter: task_filter, task_runner: task_runner
83
- )
80
+ HostExecutionStep.new(tasks: task, host: host, task_filter: task_filter, task_runner: task_runner)
84
81
  end
85
82
  steps.reject!(&:empty?)
86
83
  result << steps unless steps.empty?
@@ -7,10 +7,7 @@ module Tomo
7
7
  @concurrency = concurrency
8
8
  end
9
9
 
10
- # rubocop:disable Metrics/MethodLength
11
- # rubocop:disable Metrics/AbcSize
12
- # rubocop:disable Metrics/CyclomaticComplexity
13
- def to_s
10
+ def to_s # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
14
11
  desc = []
15
12
  threads = [applicable_hosts.length, concurrency].min
16
13
  desc << "CONCURRENTLY (#{threads} THREADS):" if threads > 1
@@ -32,9 +29,6 @@ module Tomo
32
29
  end
33
30
  desc.join("\n")
34
31
  end
35
- # rubocop:enable Metrics/MethodLength
36
- # rubocop:enable Metrics/AbcSize
37
- # rubocop:enable Metrics/CyclomaticComplexity
38
32
 
39
33
  private
40
34
 
@@ -35,9 +35,7 @@ module Tomo
35
35
  end
36
36
 
37
37
  def symbolize(hash)
38
- hash.each_with_object({}) do |(key, value), symbolized|
39
- symbolized[key.to_sym] = value
40
- end
38
+ hash.transform_keys(&:to_sym)
41
39
  end
42
40
 
43
41
  def dump_settings(hash)
@@ -21,9 +21,7 @@ module Tomo
21
21
  private
22
22
 
23
23
  def settings_sentence
24
- if settings.length == 1
25
- return "a value for the #{yellow(settings.first.to_s)} setting."
26
- end
24
+ return "a value for the #{yellow(settings.first.to_s)} setting." if settings.length == 1
27
25
 
28
26
  sentence = "values for these settings:\n\n "
29
27
  sentence << settings.map { |s| yellow(s.to_s) }.join("\n ")
@@ -19,11 +19,7 @@ module Tomo
19
19
  def validate_task!(name)
20
20
  return if tasks_by_name.key?(name)
21
21
 
22
- UnknownTaskError.raise_with(
23
- name,
24
- unknown_task: name,
25
- known_tasks: tasks_by_name.keys
26
- )
22
+ UnknownTaskError.raise_with(name, unknown_task: name, known_tasks: tasks_by_name.keys)
27
23
  end
28
24
 
29
25
  def run(task:, remote:)
@@ -53,8 +49,7 @@ module Tomo
53
49
  attr_reader :helper_modules, :tasks_by_name
54
50
 
55
51
  def ssh_options
56
- # TODO: replace with Hash#slice after dropping Ruby 2.4 support
57
- settings.select { |key| SSH::Options::DEFAULTS.key?(key) }
52
+ settings.slice(*SSH::Options::DEFAULTS.keys)
58
53
  end
59
54
  end
60
55
  end
@@ -17,10 +17,7 @@ module Tomo
17
17
  private
18
18
 
19
19
  def spelling_suggestion
20
- sugg = Error::Suggestions.new(
21
- dictionary: known_tasks,
22
- word: unknown_task
23
- )
20
+ sugg = Error::Suggestions.new(dictionary: known_tasks, word: unknown_task)
24
21
  sugg.to_console if sugg.any?
25
22
  end
26
23