kdeploy 1.2.30 → 1.2.38

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: 4bb0be08200c1be7fccc54f427d3dfc8111113bde76217642cb18a5a1d7bb6ea
4
- data.tar.gz: 2002567662d65d4d6927e2090b56ce88e8acfb5d64eb758c4a5b92a06bf3be95
3
+ metadata.gz: 29a2ac38f02097f22a48646d3d6483b4e3fd65513f01eb6044ba7efd85f9e51f
4
+ data.tar.gz: 27d6af727a2929a8fe7ec7bca6e7b63a8b2acc6a373ab73baa89b536fbb76941
5
5
  SHA512:
6
- metadata.gz: 2c9f208415d75f9edd4b06ec15f8ff25e730f2a2b0a8356b6d10a15db441a32a275544e8efc822edbeba28eebad3528093c2fff0baf15378a579fd508e2fe250
7
- data.tar.gz: eea102b4f5628f869299258fbc775b5c95b1a3c8105cedb2dedd731915016c02714689f14015b105cf38fa39505d4e14c4aa8080fdbf36cd66760b3a1eaa685b
6
+ metadata.gz: 6ec34aac0c945e25101d9a3d91b68121660a5ab16d47cb7d8cb736805d83ef4f09fd92701351051449ecf6427f85f955a93384ef99cffebebeae09cf1b9a11e1
7
+ data.tar.gz: 416f3adfc65db0a5534cafc2853296fe585742470a482802ad08156f13acb1259ff86bf33c5a10c6c5d671eaae2b04b5a8de3c1f2bdfb0e82646a58bc3512cfd
data/AGENTS.md ADDED
@@ -0,0 +1,18 @@
1
+ <!-- OPENSPEC:START -->
2
+ # OpenSpec Instructions
3
+
4
+ These instructions are for AI assistants working in this project.
5
+
6
+ Always open `@/openspec/AGENTS.md` when the request:
7
+ - Mentions planning or proposals (words like proposal, spec, change, plan)
8
+ - Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
9
+ - Sounds ambiguous and you need the authoritative spec before coding
10
+
11
+ Use `@/openspec/AGENTS.md` to learn:
12
+ - How to create and apply change proposals
13
+ - Spec format and conventions
14
+ - Project structure and guidelines
15
+
16
+ Keep this managed block so 'openspec update' can refresh the instructions.
17
+
18
+ <!-- OPENSPEC:END -->
data/README.md CHANGED
@@ -201,6 +201,11 @@ kdeploy execute deploy.rb deploy_web
201
201
  - `--limit HOSTS`: 限制执行到特定主机(逗号分隔)
202
202
  - `--parallel NUM`: 并行执行数量(默认: 10)
203
203
  - `--dry-run`: 预览模式 - 显示将要执行的操作而不实际执行
204
+ - `--debug`: 调试模式 - 显示 `run` 命令的 stdout/stderr 详细输出(便于排查问题)
205
+ - `--no-banner`: 不输出 Banner(更适合脚本/CI 场景)
206
+ - `--format FORMAT`: 输出格式(`text`|`json`,默认 `text`)
207
+ - `--retries N`: 网络相关操作重试次数(默认 `0`)
208
+ - `--retry-delay SECONDS`: 每次重试间隔秒数(默认 `1`)
204
209
 
205
210
  **示例:**
206
211
  ```bash
@@ -213,6 +218,15 @@ kdeploy execute deploy.rb deploy_web --limit web01,web02
213
218
  # 使用自定义并行数量
214
219
  kdeploy execute deploy.rb deploy_web --parallel 5
215
220
 
221
+ # 输出详细调试信息(stdout/stderr)
222
+ kdeploy execute deploy.rb deploy_web --debug
223
+
224
+ # 机器可读 JSON 输出(便于集成)
225
+ kdeploy execute deploy.rb deploy_web --format json --no-banner
226
+
227
+ # 重试网络抖动导致的失败
228
+ kdeploy execute deploy.rb deploy_web --retries 3 --retry-delay 1
229
+
216
230
  # 组合选项
217
231
  kdeploy execute deploy.rb deploy_web --limit web01 --parallel 3 --dry-run
218
232
  ```
@@ -515,6 +529,67 @@ sync "./config", "/etc/app",
515
529
  - 同步静态资源文件
516
530
  - 保持本地和远程目录结构一致
517
531
 
532
+ ### Chef 风格资源 DSL
533
+
534
+ Kdeploy 提供类似 Chef 的声明式资源 DSL,可替代或与底层原语(`run`、`upload`、`upload_template`)混用。
535
+
536
+ #### `package` - 安装系统包
537
+
538
+ ```ruby
539
+ package "nginx"
540
+ package "nginx", version: "1.18"
541
+ package "nginx", platform: :yum # CentOS/RHEL
542
+ ```
543
+
544
+ 默认使用 apt(Ubuntu/Debian);`platform: :yum` 生成 yum 命令。
545
+
546
+ #### `service` - 管理系统服务(systemd)
547
+
548
+ ```ruby
549
+ service "nginx", action: [:enable, :start]
550
+ service "nginx", action: :restart
551
+ service "nginx", action: [:stop, :disable]
552
+ ```
553
+
554
+ 支持 `:start`、`:stop`、`:restart`、`:reload`、`:enable`、`:disable`。
555
+
556
+ #### `template` - 部署 ERB 模板
557
+
558
+ ```ruby
559
+ template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
560
+ # 或 block 语法
561
+ template "/etc/app.conf" do
562
+ source "./config/app.erb"
563
+ variables(domain: "example.com")
564
+ end
565
+ ```
566
+
567
+ #### `file` - 上传本地文件
568
+
569
+ ```ruby
570
+ file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
571
+ ```
572
+
573
+ #### `directory` - 确保远程目录存在
574
+
575
+ ```ruby
576
+ directory "/etc/nginx/conf.d"
577
+ directory "/var/log/app", mode: "0755"
578
+ ```
579
+
580
+ **示例:使用资源 DSL 部署 Nginx**
581
+
582
+ ```ruby
583
+ task :deploy_nginx, roles: :web do
584
+ package "nginx"
585
+ directory "/etc/nginx/conf.d"
586
+ template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
587
+ file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
588
+ run "nginx -t"
589
+ service "nginx", action: [:enable, :restart]
590
+ end
591
+ ```
592
+
518
593
  ### 模板支持
519
594
 
520
595
  Kdeploy 支持 ERB(嵌入式 Ruby)模板,用于动态配置生成。
@@ -861,7 +936,6 @@ kdeploy execute deploy.rb deploy --parallel 3
861
936
  - **Executor** (`executor.rb`): SSH/SCP 执行引擎
862
937
  - **Runner** (`runner.rb`): 并发任务执行协调器
863
938
  - **CommandExecutor** (`command_executor.rb`): 单个命令执行
864
- - **CommandGrouper** (`command_grouper.rb`): 命令分组逻辑
865
939
  - **Template** (`template.rb`): ERB 模板渲染
866
940
  - **Output** (`output.rb`): 输出格式化和显示
867
941
  - **Configuration** (`configuration.rb`): 配置管理
@@ -871,10 +945,9 @@ kdeploy execute deploy.rb deploy --parallel 3
871
945
 
872
946
  1. **解析配置**: 加载并解析 `deploy.rb`
873
947
  2. **解析主机**: 根据任务定义确定目标主机
874
- 3. **分组命令**: 按类型分组命令以提高执行效率
875
- 4. **并发执行**: 跨主机并行运行任务
876
- 5. **收集结果**: 收集执行结果和状态
877
- 6. **显示输出**: 格式化并向用户显示结果
948
+ 3. **并发执行**: 跨主机并行运行任务,按序执行每台主机上的命令
949
+ 4. **收集结果**: 收集执行结果和状态
950
+ 5. **显示输出**: 格式化并向用户显示结果
878
951
 
879
952
  ### 并发模型
880
953
 
@@ -914,7 +987,6 @@ kdeploy/
914
987
  │ ├── executor.rb # SSH/SCP 执行器
915
988
  │ ├── runner.rb # 任务运行器
916
989
  │ ├── command_executor.rb # 命令执行器
917
- │ ├── command_grouper.rb # 命令分组器
918
990
  │ ├── template.rb # 模板处理器
919
991
  │ ├── output.rb # 输出接口
920
992
  │ ├── configuration.rb # 配置
data/README_EN.md CHANGED
@@ -200,6 +200,11 @@ kdeploy execute deploy.rb deploy_web
200
200
  - `--limit HOSTS`: Limit execution to specific hosts (comma-separated)
201
201
  - `--parallel NUM`: Number of parallel executions (default: 10)
202
202
  - `--dry-run`: Preview mode - show what would be done without executing
203
+ - `--debug`: Debug mode - show detailed stdout/stderr output for `run` steps
204
+ - `--no-banner`: Do not print banner (automation-friendly)
205
+ - `--format FORMAT`: Output format (`text`|`json`, default `text`)
206
+ - `--retries N`: Retry count for network operations (default `0`)
207
+ - `--retry-delay SECONDS`: Delay between retries in seconds (default `1`)
203
208
 
204
209
  **Examples:**
205
210
  ```bash
@@ -212,6 +217,15 @@ kdeploy execute deploy.rb deploy_web --limit web01,web02
212
217
  # Use custom parallel count
213
218
  kdeploy execute deploy.rb deploy_web --parallel 5
214
219
 
220
+ # Show detailed stdout/stderr output
221
+ kdeploy execute deploy.rb deploy_web --debug
222
+
223
+ # Machine-readable JSON output
224
+ kdeploy execute deploy.rb deploy_web --format json --no-banner
225
+
226
+ # Retry transient network failures
227
+ kdeploy execute deploy.rb deploy_web --retries 3 --retry-delay 1
228
+
215
229
  # Combine options
216
230
  kdeploy execute deploy.rb deploy_web --limit web01 --parallel 3 --dry-run
217
231
  ```
@@ -472,6 +486,67 @@ upload_template "./config/nginx.conf.erb", "/etc/nginx/nginx.conf",
472
486
  - `destination`: Remote file path
473
487
  - `variables`: Hash of variables for template rendering
474
488
 
489
+ ### Chef-Style Resource DSL
490
+
491
+ Kdeploy provides a declarative resource DSL similar to Chef, which can replace or mix with low-level primitives (`run`, `upload`, `upload_template`).
492
+
493
+ #### `package` - Install System Packages
494
+
495
+ ```ruby
496
+ package "nginx"
497
+ package "nginx", version: "1.18"
498
+ package "nginx", platform: :yum # CentOS/RHEL
499
+ ```
500
+
501
+ Uses apt (Ubuntu/Debian) by default; `platform: :yum` generates yum commands.
502
+
503
+ #### `service` - Manage System Services (systemd)
504
+
505
+ ```ruby
506
+ service "nginx", action: [:enable, :start]
507
+ service "nginx", action: :restart
508
+ service "nginx", action: [:stop, :disable]
509
+ ```
510
+
511
+ Supports `:start`, `:stop`, `:restart`, `:reload`, `:enable`, `:disable`.
512
+
513
+ #### `template` - Deploy ERB Templates
514
+
515
+ ```ruby
516
+ template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
517
+ # Or block syntax
518
+ template "/etc/app.conf" do
519
+ source "./config/app.erb"
520
+ variables(domain: "example.com")
521
+ end
522
+ ```
523
+
524
+ #### `file` - Upload Local Files
525
+
526
+ ```ruby
527
+ file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
528
+ ```
529
+
530
+ #### `directory` - Ensure Remote Directory Exists
531
+
532
+ ```ruby
533
+ directory "/etc/nginx/conf.d"
534
+ directory "/var/log/app", mode: "0755"
535
+ ```
536
+
537
+ **Example: Deploy Nginx Using Resource DSL**
538
+
539
+ ```ruby
540
+ task :deploy_nginx, roles: :web do
541
+ package "nginx"
542
+ directory "/etc/nginx/conf.d"
543
+ template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
544
+ file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
545
+ run "nginx -t"
546
+ service "nginx", action: [:enable, :restart]
547
+ end
548
+ ```
549
+
475
550
  ### Template Support
476
551
 
477
552
  Kdeploy supports ERB (Embedded Ruby) templates for dynamic configuration generation.
@@ -869,7 +944,6 @@ Enable verbose output by checking the execution output. Kdeploy provides detaile
869
944
  - **Executor** (`executor.rb`): SSH/SCP execution engine
870
945
  - **Runner** (`runner.rb`): Concurrent task execution coordinator
871
946
  - **CommandExecutor** (`command_executor.rb`): Individual command execution
872
- - **CommandGrouper** (`command_grouper.rb`): Command grouping logic
873
947
  - **Template** (`template.rb`): ERB template rendering
874
948
  - **Output** (`output.rb`): Output formatting and display
875
949
  - **Configuration** (`configuration.rb`): Configuration management
@@ -879,10 +953,9 @@ Enable verbose output by checking the execution output. Kdeploy provides detaile
879
953
 
880
954
  1. **Parse Configuration**: Load and parse `deploy.rb`
881
955
  2. **Resolve Hosts**: Determine target hosts based on task definition
882
- 3. **Group Commands**: Group commands by type for efficient execution
883
- 4. **Execute Concurrently**: Run tasks in parallel across hosts
884
- 5. **Collect Results**: Gather execution results and status
885
- 6. **Display Output**: Format and display results to user
956
+ 3. **Execute Concurrently**: Run tasks in parallel across hosts, executing commands in order per host
957
+ 4. **Collect Results**: Gather execution results and status
958
+ 5. **Display Output**: Format and display results to user
886
959
 
887
960
  ### Concurrency Model
888
961
 
@@ -922,7 +995,6 @@ kdeploy/
922
995
  │ ├── executor.rb # SSH/SCP executor
923
996
  │ ├── runner.rb # Task runner
924
997
  │ ├── command_executor.rb # Command executor
925
- │ ├── command_grouper.rb # Command grouper
926
998
  │ ├── template.rb # Template handler
927
999
  │ ├── output.rb # Output interface
928
1000
  │ ├── configuration.rb # Configuration
data/exe/kdeploy CHANGED
@@ -1,19 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # 自动修复常见 gem 扩展
5
- # %w[debug rbs].zip(%w[1.7.1 2.8.2]).each do |gem_name, version|
6
- # require gem_name
7
- # rescue LoadError
8
- # warn "[Kdeploy] 自动修复 #{gem_name}-#{version} ..."
9
- # system("gem pristine #{gem_name} --version #{version}")
10
- # begin
11
- # require gem_name
12
- # rescue LoadError
13
- # warn "[Kdeploy] 依然无法加载 #{gem_name}-#{version},请手动修复。"
14
- # end
15
- # end
16
-
17
4
  require 'kdeploy'
18
-
19
5
  Kdeploy::CLI.start(ARGV)
data/lib/kdeploy/cli.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require 'json'
4
5
  require 'pastel'
5
6
  require 'tty-table'
6
7
  require 'tty-box'
@@ -43,9 +44,13 @@ module Kdeploy
43
44
 
44
45
  desc 'execute TASK_FILE [TASK]', 'Execute deployment tasks from file'
45
46
  method_option :limit, type: :string, desc: 'Limit to specific hosts (comma-separated)'
46
- method_option :parallel, type: :numeric, default: 10, desc: 'Number of parallel executions'
47
+ method_option :parallel, type: :numeric, desc: 'Number of parallel executions'
47
48
  method_option :dry_run, type: :boolean, desc: 'Show what would be done'
48
49
  method_option :debug, type: :boolean, desc: 'Show detailed command output (stdout/stderr)'
50
+ method_option :no_banner, type: :boolean, desc: 'Do not print banner'
51
+ method_option :format, type: :string, default: 'text', desc: 'Output format (text|json)'
52
+ method_option :retries, type: :numeric, desc: 'Retry count for network operations (default: 0)'
53
+ method_option :retry_delay, type: :numeric, desc: 'Retry delay seconds (default: 1)'
49
54
  def execute(task_file, task_name = nil)
50
55
  load_config_file
51
56
  show_banner_once
@@ -53,7 +58,10 @@ module Kdeploy
53
58
  load_task_file(task_file)
54
59
 
55
60
  tasks_to_run = determine_tasks(task_name)
56
- execute_tasks(tasks_to_run)
61
+ all_results = execute_tasks(tasks_to_run)
62
+
63
+ # Exit non-zero if any executed host failed.
64
+ exit 1 if any_failed?(all_results)
57
65
  rescue StandardError => e
58
66
  puts Kdeploy::Banner.show_error(e.message)
59
67
  exit 1
@@ -134,21 +142,24 @@ module Kdeploy
134
142
  print_summary(results, formatter) if show_summary
135
143
  end
136
144
 
137
- def print_host_result(_host, result, formatter)
145
+ def print_host_result(host, result, formatter)
138
146
  if %i[success changed].include?(result[:status])
139
- print_success_result(result, formatter)
147
+ print_success_result(host, result, formatter)
140
148
  else
141
- print_failure_result(result, formatter)
149
+ print_failure_result(host, result, formatter)
142
150
  end
151
+
152
+ duration = formatter.calculate_host_duration(result)
153
+ puts "#{formatter.host_prefix(host)}#{formatter.format_host_completed(duration)}" if duration.positive?
143
154
  end
144
155
 
145
- def print_success_result(result, formatter)
156
+ def print_success_result(host, result, formatter)
146
157
  shown = {}
147
158
  grouped = group_output_by_type(result[:output])
148
159
 
149
160
  grouped.each do |type, steps|
150
161
  output_lines = format_steps_by_type(type, steps, shown, formatter)
151
- output_lines.each { |line| puts line }
162
+ output_lines.each { |line| puts "#{formatter.host_prefix(host)}#{line}" }
152
163
  end
153
164
  end
154
165
 
@@ -171,9 +182,21 @@ module Kdeploy
171
182
  end
172
183
  end
173
184
 
174
- def print_failure_result(result, formatter)
185
+ def print_failure_result(host, result, formatter)
175
186
  error_message = extract_error_message(result)
176
- puts formatter.format_error(error_message)
187
+ puts "#{formatter.host_prefix(host)}#{formatter.format_error(error_message)}"
188
+
189
+ # On failure, show steps that were executed (and any captured output)
190
+ # to make troubleshooting easier, even if --debug is not enabled.
191
+ return unless result[:output].is_a?(Array) && result[:output].any?
192
+
193
+ debug_formatter = OutputFormatter.new(debug: true)
194
+ shown = {}
195
+ grouped = group_output_by_type(result[:output])
196
+ grouped.each do |type, steps|
197
+ output_lines = format_steps_by_type(type, steps, shown, debug_formatter)
198
+ output_lines.each { |line| puts "#{debug_formatter.host_prefix(host)}#{line}" }
199
+ end
177
200
  end
178
201
 
179
202
  def print_summary(results, formatter)
@@ -189,6 +212,7 @@ module Kdeploy
189
212
  def show_banner_once
190
213
  @banner_printed ||= false
191
214
  return if @banner_printed
215
+ return if options[:no_banner]
192
216
 
193
217
  puts Kdeploy::Banner.show
194
218
  @banner_printed = true
@@ -205,10 +229,15 @@ module Kdeploy
205
229
  task_results = execute_single_task(task)
206
230
  # Collect results for final summary
207
231
  all_results[task] = task_results if task_results
232
+
233
+ # Stop executing remaining tasks once any host failed for this task.
234
+ break if task_failed?(task_results)
208
235
  end
209
236
 
210
237
  # Show combined summary at the end for all tasks
211
- print_all_tasks_summary(all_results) unless all_results.empty?
238
+ print_all_tasks_summary(all_results) unless all_results.empty? || options[:format] == 'json'
239
+
240
+ all_results
212
241
  end
213
242
 
214
243
  def execute_single_task(task)
@@ -221,7 +250,11 @@ module Kdeploy
221
250
  end
222
251
 
223
252
  if options[:dry_run]
224
- print_dry_run(hosts, task)
253
+ if options[:format] == 'json'
254
+ print_dry_run_json(hosts, task)
255
+ else
256
+ print_dry_run(hosts, task)
257
+ end
225
258
  return nil
226
259
  end
227
260
 
@@ -232,20 +265,40 @@ module Kdeploy
232
265
  output = ConsoleOutput.new
233
266
  parallel_count = options[:parallel] || Configuration.default_parallel
234
267
  debug_mode = options[:debug] || false
268
+ retries = options[:retries].nil? ? Configuration.default_retries : options[:retries]
269
+ retry_delay = options[:retry_delay].nil? ? Configuration.default_retry_delay : options[:retry_delay]
235
270
  base_dir = @task_file_dir
236
271
  runner = Runner.new(
237
272
  hosts, self.class.kdeploy_tasks,
238
273
  parallel: parallel_count,
239
274
  output: output,
240
275
  debug: debug_mode,
241
- base_dir: base_dir
276
+ base_dir: base_dir,
277
+ retries: retries,
278
+ retry_delay: retry_delay
242
279
  )
243
280
  results = runner.run(task)
244
- # Don't show summary here - it will be shown at the end for all tasks
245
- print_results(results, task, show_summary: false, debug: debug_mode)
281
+ if options[:format] == 'json'
282
+ print_results_json(task, results)
283
+ else
284
+ # Don't show summary here - it will be shown at the end for all tasks
285
+ print_results(results, task, show_summary: false, debug: debug_mode)
286
+ end
246
287
  results
247
288
  end
248
289
 
290
+ def any_failed?(all_results)
291
+ all_results.values.any? do |task_results|
292
+ task_results.values.any? { |result| result[:status] == :failed }
293
+ end
294
+ end
295
+
296
+ def task_failed?(task_results)
297
+ return false unless task_results.is_a?(Hash)
298
+
299
+ task_results.values.any? { |result| result[:status] == :failed }
300
+ end
301
+
249
302
  def print_all_tasks_summary(all_results)
250
303
  debug_mode = options[:debug] || false
251
304
  formatter = OutputFormatter.new(debug: debug_mode)
@@ -272,6 +325,76 @@ module Kdeploy
272
325
  end
273
326
  end
274
327
 
328
+ def print_results_json(task_name, results)
329
+ payload = {
330
+ task: task_name.to_s,
331
+ results: results.transform_values { |r| serialize_host_result(r) }
332
+ }
333
+ puts JSON.generate(payload)
334
+ end
335
+
336
+ def print_dry_run_json(hosts, task_name)
337
+ tasks = self.class.kdeploy_tasks
338
+ commands = tasks[task_name][:block].call
339
+
340
+ payload = {
341
+ task: task_name.to_s,
342
+ dry_run: true,
343
+ planned: hosts.transform_values do |_config|
344
+ commands.map { |cmd| serialize_planned_step(cmd) }
345
+ end
346
+ }
347
+
348
+ puts JSON.generate(payload)
349
+ end
350
+
351
+ def serialize_host_result(result)
352
+ {
353
+ status: result[:status].to_s,
354
+ error: result[:error],
355
+ steps: Array(result[:output]).map { |step| serialize_step(step) }
356
+ }
357
+ end
358
+
359
+ def serialize_step(step)
360
+ out = {
361
+ type: step[:type].to_s,
362
+ command: step[:command],
363
+ duration: step[:duration]
364
+ }
365
+
366
+ out[:result] = step[:result] if step[:type] == :sync
367
+
368
+ if options[:debug] && step[:type] == :run && step[:output].is_a?(Hash)
369
+ out[:stdout] = step[:output][:stdout]
370
+ out[:stderr] = step[:output][:stderr]
371
+ end
372
+
373
+ out
374
+ end
375
+
376
+ def serialize_planned_step(cmd)
377
+ case cmd[:type]
378
+ when :run
379
+ { type: 'run', command: cmd[:command], sudo: cmd[:sudo] }
380
+ when :upload
381
+ { type: 'upload', source: cmd[:source], destination: cmd[:destination] }
382
+ when :upload_template
383
+ { type: 'upload_template', source: cmd[:source], destination: cmd[:destination], variables: cmd[:variables] }
384
+ when :sync
385
+ {
386
+ type: 'sync',
387
+ source: cmd[:source],
388
+ destination: cmd[:destination],
389
+ ignore: cmd[:ignore] || [],
390
+ exclude: cmd[:exclude] || [],
391
+ delete: cmd[:delete] || false
392
+ }
393
+ else
394
+ { type: cmd[:type].to_s }
395
+ end
396
+ end
397
+
275
398
  def extract_error_message(result)
276
399
  return result[:error] if result[:error]
277
400