cpl 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.overcommit.yml +10 -0
  3. data/Gemfile.lock +10 -3
  4. data/README.md +6 -0
  5. data/cpl.gemspec +1 -0
  6. data/docs/commands.md +51 -3
  7. data/googlee2da545df05d92f9.html +1 -0
  8. data/lib/command/base.rb +65 -7
  9. data/lib/command/build_image.rb +6 -5
  10. data/lib/command/cleanup_old_images.rb +8 -7
  11. data/lib/command/cleanup_stale_apps.rb +11 -9
  12. data/lib/command/config.rb +30 -15
  13. data/lib/command/copy_image_from_upstream.rb +110 -0
  14. data/lib/command/delete.rb +10 -12
  15. data/lib/command/deploy_image.rb +6 -5
  16. data/lib/command/env.rb +2 -2
  17. data/lib/command/exists.rb +4 -4
  18. data/lib/command/info.rb +233 -0
  19. data/lib/command/latest_image.rb +2 -2
  20. data/lib/command/logs.rb +4 -4
  21. data/lib/command/no_command.rb +3 -3
  22. data/lib/command/open.rb +4 -4
  23. data/lib/command/promote_app_from_upstream.rb +58 -0
  24. data/lib/command/ps.rb +10 -13
  25. data/lib/command/ps_restart.rb +9 -6
  26. data/lib/command/ps_start.rb +7 -6
  27. data/lib/command/ps_stop.rb +7 -6
  28. data/lib/command/run.rb +5 -5
  29. data/lib/command/run_detached.rb +7 -5
  30. data/lib/command/setup.rb +71 -13
  31. data/lib/command/test.rb +2 -2
  32. data/lib/command/version.rb +2 -2
  33. data/lib/core/config.rb +26 -19
  34. data/lib/core/controlplane.rb +77 -11
  35. data/lib/core/controlplane_api.rb +12 -0
  36. data/lib/core/controlplane_api_cli.rb +1 -1
  37. data/lib/core/controlplane_api_direct.rb +2 -2
  38. data/lib/core/shell.rb +25 -3
  39. data/lib/cpl/version.rb +1 -1
  40. data/lib/cpl.rb +19 -10
  41. data/lib/deprecated_commands.json +6 -0
  42. data/script/add_command +37 -0
  43. data/script/generate_commands_docs +5 -5
  44. data/script/rename_command +43 -0
  45. metadata +24 -2
@@ -7,14 +7,14 @@ module Command
7
7
  app_option(required: true)
8
8
  ].freeze
9
9
  DESCRIPTION = "Shell-checks if an application (GVC) exists, useful in scripts"
10
- LONG_DESCRIPTION = <<~HEREDOC
10
+ LONG_DESCRIPTION = <<~DESC
11
11
  - Shell-checks if an application (GVC) exists, useful in scripts, e.g.:
12
- HEREDOC
13
- EXAMPLES = <<~HEREDOC
12
+ DESC
13
+ EXAMPLES = <<~EX
14
14
  ```sh
15
15
  if [ cpl exists -a $APP_NAME ]; ...
16
16
  ```
17
- HEREDOC
17
+ EX
18
18
 
19
19
  def call
20
20
  exit(!cp.fetch_gvc.nil?)
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class Info < Base # rubocop:disable Metrics/ClassLength
5
+ NAME = "info"
6
+ OPTIONS = [
7
+ org_option,
8
+ app_option
9
+ ].freeze
10
+ DESCRIPTION = "Displays the diff between defined/available apps/workloads (apps equal GVCs)"
11
+ LONG_DESCRIPTION = <<~DESC
12
+ - Displays the diff between defined/available apps/workloads (apps equal GVCs)
13
+ - Apps that are defined but not available are displayed in red
14
+ - Apps that are available but not defined are displayed in green
15
+ - Apps that are both defined and available are displayed in white
16
+ - The diff is based on what's defined in the `.controlplane/controlplane.yml` file
17
+ DESC
18
+ EXAMPLES = <<~EX
19
+ ```sh
20
+ # Shows diff for all apps in all orgs.
21
+ cpl info
22
+
23
+ # Shows diff for all apps in a specific org.
24
+ cpl info -o $ORG_NAME
25
+
26
+ # Shows diff for a specific app.
27
+ cpl info -a $APP_NAME
28
+ ```
29
+ EX
30
+
31
+ def call
32
+ @missing_apps_workloads = {}
33
+ @missing_apps_starting_with = {}
34
+
35
+ if config.app && !config.current[:match_if_app_name_starts_with]
36
+ single_app_info
37
+ else
38
+ multiple_apps_info
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def app_matches?(app, app_name, app_options)
45
+ app == app_name.to_s || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s))
46
+ end
47
+
48
+ def find_app_options(app)
49
+ @app_options ||= {}
50
+ @app_options[app] ||= config.apps.find do |app_name, app_options|
51
+ app_matches?(app, app_name, app_options)
52
+ end&.last
53
+ end
54
+
55
+ def find_workloads(app)
56
+ app_options = find_app_options(app)
57
+ return [] if app_options.nil?
58
+
59
+ (app_options[:app_workloads] + app_options[:additional_workloads] + [app_options[:one_off_workload]]).uniq
60
+ end
61
+
62
+ def fetch_workloads(app)
63
+ cp.fetch_workloads(app)["items"].map { |workload| workload["name"] }
64
+ end
65
+
66
+ def fetch_app_workloads(org) # rubocop:disable Metrics/MethodLength
67
+ result = {}
68
+
69
+ workloads = cp.fetch_workloads_by_org(org)["items"]
70
+ workloads.each do |workload|
71
+ app = workload["links"].find { |link| link["rel"] == "gvc" }["href"].split("/").last
72
+
73
+ result[app] ||= []
74
+ result[app].push(workload["name"])
75
+ end
76
+
77
+ if config.app
78
+ result.select { |app, _| app_matches?(app, config.app, config.current) }
79
+ else
80
+ result.reject { |app, _| find_app_options(app).nil? }
81
+ end
82
+ end
83
+
84
+ def orgs # rubocop:disable Metrics/MethodLength
85
+ result = []
86
+
87
+ if config.options[:org]
88
+ result.push(config.options[:org])
89
+ else
90
+ config.apps.each do |app_name, app_options|
91
+ next if config.app && !app_matches?(config.app, app_name, app_options)
92
+
93
+ org = app_options[:cpln_org] || app_options[:org]
94
+ result.push(org) unless result.include?(org)
95
+ end
96
+ end
97
+
98
+ result.sort
99
+ end
100
+
101
+ def apps(org)
102
+ result = []
103
+
104
+ config.apps.each do |app_name, app_options|
105
+ next if config.app && !app_matches?(config.app, app_name, app_options)
106
+
107
+ app_org = app_options[:cpln_org] || app_options[:org]
108
+ result.push(app_name.to_s) if app_org == org
109
+ end
110
+
111
+ result += @app_workloads.keys.map(&:to_s)
112
+ result.uniq.sort
113
+ end
114
+
115
+ def should_app_start_with?(app)
116
+ config.apps[app.to_sym]&.dig(:match_if_app_name_starts_with)
117
+ end
118
+
119
+ def any_app_starts_with?(app)
120
+ @app_workloads.keys.find { |app_name| app_matches?(app_name, app, config.apps[app.to_sym]) }
121
+ end
122
+
123
+ def check_any_app_starts_with(app)
124
+ if any_app_starts_with?(app)
125
+ false
126
+ else
127
+ @missing_apps_starting_with[app] ||= ["gvc"]
128
+
129
+ puts " - #{Shell.color("Any app starting with '#{app}'", :red)}"
130
+ true
131
+ end
132
+ end
133
+
134
+ def add_to_missing_workloads(app, workload)
135
+ if should_app_start_with?(app)
136
+ @missing_apps_starting_with[app] ||= []
137
+ @missing_apps_starting_with[app].push(workload)
138
+ else
139
+ @missing_apps_workloads[app] ||= []
140
+ @missing_apps_workloads[app].push(workload)
141
+ end
142
+ end
143
+
144
+ def print_app(app, org)
145
+ if should_app_start_with?(app)
146
+ check_any_app_starts_with(app)
147
+ elsif cp.fetch_gvc(app, org).nil?
148
+ @missing_apps_workloads[app] = ["gvc"]
149
+
150
+ puts " - #{Shell.color(app, :red)}"
151
+ true
152
+ else
153
+ puts " - #{app}"
154
+ true
155
+ end
156
+ end
157
+
158
+ def print_workload(app, workload)
159
+ if @defined_workloads.include?(workload) && !@available_workloads.include?(workload)
160
+ add_to_missing_workloads(app, workload)
161
+
162
+ puts " - #{Shell.color(workload, :red)}"
163
+ elsif !@defined_workloads.include?(workload) && @available_workloads.include?(workload)
164
+ puts " - #{Shell.color(workload, :green)}"
165
+ else
166
+ puts " - #{workload}"
167
+ end
168
+ end
169
+
170
+ def print_missing_apps_workloads
171
+ return if @missing_apps_workloads.empty?
172
+
173
+ puts "\nSome apps/workloads are missing. Please create them with:"
174
+
175
+ @missing_apps_workloads.each do |app, workloads|
176
+ puts " - `cpl setup #{workloads.join(' ')} -a #{app}`"
177
+ end
178
+ end
179
+
180
+ def print_missing_apps_starting_with
181
+ return if @missing_apps_starting_with.empty?
182
+
183
+ puts "\nThere are no apps starting with some names. If you wish to create any, do so with " \
184
+ "(replace 'whatever' with whatever suffix you want):"
185
+
186
+ @missing_apps_starting_with.each do |app, workloads|
187
+ app_with_suffix = "#{app}#{app.end_with?('-') ? '' : '-'}whatever"
188
+ puts " - `cpl setup #{workloads.join(' ')} -a #{app_with_suffix}`"
189
+ end
190
+ end
191
+
192
+ def single_app_info
193
+ puts "#{Shell.color(config.org, :blue)}:"
194
+
195
+ print_app(config.app, config.org)
196
+
197
+ @defined_workloads = find_workloads(config.app)
198
+ @available_workloads = fetch_workloads(config.app)
199
+
200
+ workloads = (@defined_workloads + @available_workloads).uniq.sort
201
+ workloads.each do |workload|
202
+ print_workload(config.app, workload)
203
+ end
204
+
205
+ print_missing_apps_workloads
206
+ end
207
+
208
+ def multiple_apps_info # rubocop:disable Metrics/MethodLength
209
+ orgs.each do |org|
210
+ puts "#{Shell.color(org, :blue)}:"
211
+
212
+ @app_workloads = fetch_app_workloads(org)
213
+
214
+ apps(org).each do |app|
215
+ next unless print_app(app, org)
216
+
217
+ @defined_workloads = find_workloads(app)
218
+ @available_workloads = @app_workloads[app] || []
219
+
220
+ workloads = (@defined_workloads + @available_workloads).uniq.sort
221
+ workloads.each do |workload|
222
+ print_workload(app, workload)
223
+ end
224
+ end
225
+
226
+ puts
227
+ end
228
+
229
+ print_missing_apps_workloads
230
+ print_missing_apps_starting_with
231
+ end
232
+ end
233
+ end
@@ -7,9 +7,9 @@ module Command
7
7
  app_option(required: true)
8
8
  ].freeze
9
9
  DESCRIPTION = "Displays the latest image name"
10
- LONG_DESCRIPTION = <<~HEREDOC
10
+ LONG_DESCRIPTION = <<~DESC
11
11
  - Displays the latest image name
12
- HEREDOC
12
+ DESC
13
13
 
14
14
  def call
15
15
  puts latest_image
data/lib/command/logs.rb CHANGED
@@ -8,10 +8,10 @@ module Command
8
8
  workload_option
9
9
  ].freeze
10
10
  DESCRIPTION = "Light wrapper to display tailed raw logs for app/workload syntax"
11
- LONG_DESCRIPTION = <<~HEREDOC
11
+ LONG_DESCRIPTION = <<~DESC
12
12
  - Light wrapper to display tailed raw logs for app/workload syntax
13
- HEREDOC
14
- EXAMPLES = <<~HEREDOC
13
+ DESC
14
+ EXAMPLES = <<~EX
15
15
  ```sh
16
16
  # Displays logs for the default workload (`one_off_workload`).
17
17
  cpl logs -a $APP_NAME
@@ -19,7 +19,7 @@ module Command
19
19
  # Displays logs for a specific workload.
20
20
  cpl logs -a $APP_NAME -w $WORKLOAD_NAME
21
21
  ```
22
- HEREDOC
22
+ EX
23
23
 
24
24
  def call
25
25
  workload = config.options[:workload] || config[:one_off_workload]
@@ -5,15 +5,15 @@ module Command
5
5
  NAME = "no-command"
6
6
  OPTIONS = [version_option].freeze
7
7
  DESCRIPTION = "Called when no command was specified"
8
- LONG_DESCRIPTION = <<~HEREDOC
8
+ LONG_DESCRIPTION = <<~DESC
9
9
  - Called when no command was specified
10
- HEREDOC
10
+ DESC
11
11
  HIDE = true
12
12
 
13
13
  def call
14
14
  return unless config.options[:version]
15
15
 
16
- Version.new(config).call
16
+ perform("cpl version")
17
17
  end
18
18
  end
19
19
  end
data/lib/command/open.rb CHANGED
@@ -8,10 +8,10 @@ module Command
8
8
  workload_option
9
9
  ].freeze
10
10
  DESCRIPTION = "Opens the app endpoint URL in the default browser"
11
- LONG_DESCRIPTION = <<~HEREDOC
11
+ LONG_DESCRIPTION = <<~DESC
12
12
  - Opens the app endpoint URL in the default browser
13
- HEREDOC
14
- EXAMPLES = <<~HEREDOC
13
+ DESC
14
+ EXAMPLES = <<~EX
15
15
  ```sh
16
16
  # Opens the endpoint of the default workload (`one_off_workload`).
17
17
  cpl open -a $APP_NAME
@@ -19,7 +19,7 @@ module Command
19
19
  # Opens the endpoint of a specific workload.
20
20
  cpl open -a $APP_NAME -w $WORKLOAD_NAME
21
21
  ```
22
- HEREDOC
22
+ EX
23
23
 
24
24
  def call
25
25
  workload = config.options[:workload] || config[:one_off_workload]
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class PromoteAppFromUpstream < Base
5
+ NAME = "promote-app-from-upstream"
6
+ OPTIONS = [
7
+ app_option(required: true),
8
+ upstream_token_option(required: true)
9
+ ].freeze
10
+ DESCRIPTION = "Copies the latest image from upstream, runs a release script (optional), and deploys the image"
11
+ LONG_DESCRIPTION = <<~DESC
12
+ - Copies the latest image from upstream, runs a release script (optional), and deploys the image
13
+ - It performs the following steps:
14
+ - Runs `cpl copy-image-from-upstream` to copy the latest image from upstream
15
+ - Runs a release script if specified through `release_script` in the `.controlplane/controlplane.yml` file
16
+ - Runs `cpl deploy-image` to deploy the image
17
+ DESC
18
+
19
+ def call
20
+ check_release_script
21
+ copy_image_from_upstream
22
+ run_release_script
23
+ deploy_image
24
+ end
25
+
26
+ private
27
+
28
+ def check_release_script
29
+ release_script_name = config.current[:release_script]
30
+ unless release_script_name
31
+ progress.puts("Can't find option 'release_script' for app '#{config.app}' in 'controlplane.yml'. " \
32
+ "Skipping release script.\n\n")
33
+ return
34
+ end
35
+
36
+ @release_script_path = Pathname.new("#{config.app_cpln_dir}/#{release_script_name}").expand_path
37
+
38
+ raise "Can't find release script in '#{@release_script_path}'." unless File.exist?(@release_script_path)
39
+ end
40
+
41
+ def copy_image_from_upstream
42
+ perform("cpl copy-image-from-upstream -a #{config.app} -t #{config.options[:upstream_token]}")
43
+ progress.puts
44
+ end
45
+
46
+ def run_release_script
47
+ return unless @release_script_path
48
+
49
+ progress.puts("Running release script...\n\n")
50
+ perform("bash #{@release_script_path}")
51
+ progress.puts
52
+ end
53
+
54
+ def deploy_image
55
+ perform("cpl deploy-image -a #{config.app}")
56
+ end
57
+ end
58
+ end
data/lib/command/ps.rb CHANGED
@@ -8,10 +8,10 @@ module Command
8
8
  workload_option
9
9
  ].freeze
10
10
  DESCRIPTION = "Shows running replicas in app"
11
- LONG_DESCRIPTION = <<~HEREDOC
11
+ LONG_DESCRIPTION = <<~DESC
12
12
  - Shows running replicas in app
13
- HEREDOC
14
- EXAMPLES = <<~HEREDOC
13
+ DESC
14
+ EXAMPLES = <<~EX
15
15
  ```sh
16
16
  # Shows running replicas in app, for all workloads.
17
17
  cpl ps -a $APP_NAME
@@ -19,21 +19,18 @@ module Command
19
19
  # Shows running replicas in app, for a specific workload.
20
20
  cpl ps -a $APP_NAME -w $WORKLOAD_NAME
21
21
  ```
22
- HEREDOC
22
+ EX
23
+
24
+ def call
25
+ cp.fetch_gvc!
23
26
 
24
- def call # rubocop:disable Metrics/MethodLength
25
27
  workloads = [config.options[:workload]] if config.options[:workload]
26
28
  workloads ||= config[:app_workloads] + config[:additional_workloads]
27
-
28
29
  workloads.each do |workload|
30
+ cp.fetch_workload!(workload)
31
+
29
32
  result = cp.workload_get_replicas(workload, location: config[:default_location])
30
- if result.nil?
31
- puts "#{workload}: no workload"
32
- elsif result["items"].nil?
33
- puts "#{workload}: no replicas"
34
- else
35
- result["items"].each { |replica| puts replica }
36
- end
33
+ result["items"].each { |replica| puts replica }
37
34
  end
38
35
  end
39
36
  end
@@ -8,10 +8,10 @@ module Command
8
8
  workload_option
9
9
  ].freeze
10
10
  DESCRIPTION = "Forces redeploy of workloads in app"
11
- LONG_DESCRIPTION = <<~HEREDOC
11
+ LONG_DESCRIPTION = <<~DESC
12
12
  - Forces redeploy of workloads in app
13
- HEREDOC
14
- EXAMPLES = <<~HEREDOC
13
+ DESC
14
+ EXAMPLES = <<~EX
15
15
  ```sh
16
16
  # Forces redeploy of all workloads in app.
17
17
  cpl ps:restart -a $APP_NAME
@@ -19,15 +19,18 @@ module Command
19
19
  # Forces redeploy of a specific workload in app.
20
20
  cpl ps:restart -a $APP_NAME -w $WORKLOAD_NAME
21
21
  ```
22
- HEREDOC
22
+ EX
23
23
 
24
24
  def call
25
25
  workloads = [config.options[:workload]] if config.options[:workload]
26
26
  workloads ||= config[:app_workloads] + config[:additional_workloads]
27
27
 
28
28
  workloads.each do |workload|
29
- cp.workload_force_redeployment(workload)
30
- progress.puts "#{workload} restarted"
29
+ step("Restarting workload '#{workload}'") do
30
+ cp.fetch_workload!(workload)
31
+
32
+ cp.workload_force_redeployment(workload)
33
+ end
31
34
  end
32
35
  end
33
36
  end
@@ -8,10 +8,10 @@ module Command
8
8
  workload_option
9
9
  ].freeze
10
10
  DESCRIPTION = "Starts workloads in app"
11
- LONG_DESCRIPTION = <<~HEREDOC
11
+ LONG_DESCRIPTION = <<~DESC
12
12
  - Starts workloads in app
13
- HEREDOC
14
- EXAMPLES = <<~HEREDOC
13
+ DESC
14
+ EXAMPLES = <<~EX
15
15
  ```sh
16
16
  # Starts all workloads in app.
17
17
  cpl ps:start -a $APP_NAME
@@ -19,15 +19,16 @@ module Command
19
19
  # Starts a specific workload in app.
20
20
  cpl ps:start -a $APP_NAME -w $WORKLOAD_NAME
21
21
  ```
22
- HEREDOC
22
+ EX
23
23
 
24
24
  def call
25
25
  workloads = [config.options[:workload]] if config.options[:workload]
26
26
  workloads ||= config[:app_workloads] + config[:additional_workloads]
27
27
 
28
28
  workloads.reverse_each do |workload|
29
- cp.workload_set_suspend(workload, false)
30
- progress.puts "#{workload} started"
29
+ step("Starting workload '#{workload}'") do
30
+ cp.workload_set_suspend(workload, false)
31
+ end
31
32
  end
32
33
  end
33
34
  end
@@ -8,10 +8,10 @@ module Command
8
8
  workload_option
9
9
  ].freeze
10
10
  DESCRIPTION = "Stops workloads in app"
11
- LONG_DESCRIPTION = <<~HEREDOC
11
+ LONG_DESCRIPTION = <<~DESC
12
12
  - Stops workloads in app
13
- HEREDOC
14
- EXAMPLES = <<~HEREDOC
13
+ DESC
14
+ EXAMPLES = <<~EX
15
15
  ```sh
16
16
  # Stops all workloads in app.
17
17
  cpl ps:stop -a $APP_NAME
@@ -19,15 +19,16 @@ module Command
19
19
  # Stops a specific workload in app.
20
20
  cpl ps:stop -a $APP_NAME -w $WORKLOAD_NAME
21
21
  ```
22
- HEREDOC
22
+ EX
23
23
 
24
24
  def call
25
25
  workloads = [config.options[:workload]] if config.options[:workload]
26
26
  workloads ||= config[:app_workloads] + config[:additional_workloads]
27
27
 
28
28
  workloads.each do |workload|
29
- cp.workload_set_suspend(workload, true)
30
- progress.puts "#{workload} stopped"
29
+ step("Stopping workload '#{workload}'") do
30
+ cp.workload_set_suspend(workload, true)
31
+ end
31
32
  end
32
33
  end
33
34
  end
data/lib/command/run.rb CHANGED
@@ -11,15 +11,15 @@ module Command
11
11
  image_option
12
12
  ].freeze
13
13
  DESCRIPTION = "Runs one-off **_interactive_** replicas (analog of `heroku run`)"
14
- LONG_DESCRIPTION = <<~HEREDOC
14
+ LONG_DESCRIPTION = <<~DESC
15
15
  - Runs one-off **_interactive_** replicas (analog of `heroku run`)
16
16
  - Uses `Standard` workload type and `cpln exec` as the execution method, with CLI streaming
17
17
  - May not work correctly with tasks that last over 5 minutes (there's a Control Plane scaling bug at the moment)
18
18
 
19
19
  > **IMPORTANT:** Useful for development where it's needed for interaction, and where network connection drops and
20
20
  > task crashing are tolerable. For production tasks, it's better to use `cpl run:detached`.
21
- HEREDOC
22
- EXAMPLES = <<~HEREDOC
21
+ DESC
22
+ EXAMPLES = <<~EX
23
23
  ```sh
24
24
  # Opens shell (bash by default).
25
25
  cpl run -a $APP_NAME
@@ -35,7 +35,7 @@ module Command
35
35
  cpl run rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
36
36
  cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential image
37
37
  ```
38
- HEREDOC
38
+ EX
39
39
 
40
40
  attr_reader :location, :workload, :one_off
41
41
 
@@ -80,7 +80,7 @@ module Command
80
80
 
81
81
  # Override image if specified
82
82
  image = config.options[:image]
83
- image = "/org/#{config[:cpln_org]}/image/#{latest_image}" if image == "latest"
83
+ image = "/org/#{config.org}/image/#{latest_image}" if image == "latest"
84
84
  container["image"] = image if image
85
85
 
86
86
  # Set runner
@@ -10,16 +10,18 @@ module Command
10
10
  image_option
11
11
  ].freeze
12
12
  DESCRIPTION = "Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)"
13
- LONG_DESCRIPTION = <<~HEREDOC
13
+ LONG_DESCRIPTION = <<~DESC
14
14
  - Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)
15
15
  - Uses `Cron` workload type with log async fetching
16
16
  - Implemented with only async execution methods, more suitable for production tasks
17
17
  - Has alternative log fetch implementation with only JSON-polling and no WebSockets
18
18
  - Less responsive but more stable, useful for CI tasks
19
- HEREDOC
20
- EXAMPLES = <<~HEREDOC
19
+ DESC
20
+ EXAMPLES = <<~EX
21
21
  ```sh
22
22
  cpl run:detached rails db:prepare -a $APP_NAME
23
+
24
+ # Need to quote COMMAND if setting ENV value or passing args to command to run
23
25
  cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
24
26
 
25
27
  # Uses some other image.
@@ -32,7 +34,7 @@ module Command
32
34
  cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
33
35
  cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
34
36
  ```
35
- HEREDOC
37
+ EX
36
38
 
37
39
  WORKLOAD_SLEEP_CHECK = 2
38
40
 
@@ -77,7 +79,7 @@ module Command
77
79
 
78
80
  # Override image if specified
79
81
  image = config.options[:image]
80
- image = "/org/#{config[:cpln_org]}/image/#{latest_image}" if image == "latest"
82
+ image = "/org/#{config.org}/image/#{latest_image}" if image == "latest"
81
83
  container["image"] = image if image
82
84
 
83
85
  # Set cron job props