kdeploy 1.2.33 → 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 +4 -4
- data/README.md +64 -6
- data/README_EN.md +64 -6
- data/lib/kdeploy/cli.rb +31 -7
- data/lib/kdeploy/command_executor.rb +4 -48
- data/lib/kdeploy/dsl.rb +88 -0
- data/lib/kdeploy/errors.rb +6 -2
- data/lib/kdeploy/executor.rb +23 -3
- data/lib/kdeploy/initializer.rb +22 -30
- data/lib/kdeploy/output_formatter.rb +49 -43
- data/lib/kdeploy/runner.rb +41 -44
- data/lib/kdeploy/version.rb +1 -1
- data/lib/kdeploy.rb +0 -1
- data/r.md +1 -0
- metadata +2 -3
- data/lib/kdeploy/command_grouper.rb +0 -38
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29a2ac38f02097f22a48646d3d6483b4e3fd65513f01eb6044ba7efd85f9e51f
|
|
4
|
+
data.tar.gz: 27d6af727a2929a8fe7ec7bca6e7b63a8b2acc6a373ab73baa89b536fbb76941
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ec34aac0c945e25101d9a3d91b68121660a5ab16d47cb7d8cb736805d83ef4f09fd92701351051449ecf6427f85f955a93384ef99cffebebeae09cf1b9a11e1
|
|
7
|
+
data.tar.gz: 416f3adfc65db0a5534cafc2853296fe585742470a482802ad08156f13acb1259ff86bf33c5a10c6c5d671eaae2b04b5a8de3c1f2bdfb0e82646a58bc3512cfd
|
data/README.md
CHANGED
|
@@ -529,6 +529,67 @@ sync "./config", "/etc/app",
|
|
|
529
529
|
- 同步静态资源文件
|
|
530
530
|
- 保持本地和远程目录结构一致
|
|
531
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
|
+
|
|
532
593
|
### 模板支持
|
|
533
594
|
|
|
534
595
|
Kdeploy 支持 ERB(嵌入式 Ruby)模板,用于动态配置生成。
|
|
@@ -875,7 +936,6 @@ kdeploy execute deploy.rb deploy --parallel 3
|
|
|
875
936
|
- **Executor** (`executor.rb`): SSH/SCP 执行引擎
|
|
876
937
|
- **Runner** (`runner.rb`): 并发任务执行协调器
|
|
877
938
|
- **CommandExecutor** (`command_executor.rb`): 单个命令执行
|
|
878
|
-
- **CommandGrouper** (`command_grouper.rb`): 命令分组逻辑
|
|
879
939
|
- **Template** (`template.rb`): ERB 模板渲染
|
|
880
940
|
- **Output** (`output.rb`): 输出格式化和显示
|
|
881
941
|
- **Configuration** (`configuration.rb`): 配置管理
|
|
@@ -885,10 +945,9 @@ kdeploy execute deploy.rb deploy --parallel 3
|
|
|
885
945
|
|
|
886
946
|
1. **解析配置**: 加载并解析 `deploy.rb`
|
|
887
947
|
2. **解析主机**: 根据任务定义确定目标主机
|
|
888
|
-
3.
|
|
889
|
-
4.
|
|
890
|
-
5.
|
|
891
|
-
6. **显示输出**: 格式化并向用户显示结果
|
|
948
|
+
3. **并发执行**: 跨主机并行运行任务,按序执行每台主机上的命令
|
|
949
|
+
4. **收集结果**: 收集执行结果和状态
|
|
950
|
+
5. **显示输出**: 格式化并向用户显示结果
|
|
892
951
|
|
|
893
952
|
### 并发模型
|
|
894
953
|
|
|
@@ -928,7 +987,6 @@ kdeploy/
|
|
|
928
987
|
│ ├── executor.rb # SSH/SCP 执行器
|
|
929
988
|
│ ├── runner.rb # 任务运行器
|
|
930
989
|
│ ├── command_executor.rb # 命令执行器
|
|
931
|
-
│ ├── command_grouper.rb # 命令分组器
|
|
932
990
|
│ ├── template.rb # 模板处理器
|
|
933
991
|
│ ├── output.rb # 输出接口
|
|
934
992
|
│ ├── configuration.rb # 配置
|
data/README_EN.md
CHANGED
|
@@ -486,6 +486,67 @@ upload_template "./config/nginx.conf.erb", "/etc/nginx/nginx.conf",
|
|
|
486
486
|
- `destination`: Remote file path
|
|
487
487
|
- `variables`: Hash of variables for template rendering
|
|
488
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
|
+
|
|
489
550
|
### Template Support
|
|
490
551
|
|
|
491
552
|
Kdeploy supports ERB (Embedded Ruby) templates for dynamic configuration generation.
|
|
@@ -883,7 +944,6 @@ Enable verbose output by checking the execution output. Kdeploy provides detaile
|
|
|
883
944
|
- **Executor** (`executor.rb`): SSH/SCP execution engine
|
|
884
945
|
- **Runner** (`runner.rb`): Concurrent task execution coordinator
|
|
885
946
|
- **CommandExecutor** (`command_executor.rb`): Individual command execution
|
|
886
|
-
- **CommandGrouper** (`command_grouper.rb`): Command grouping logic
|
|
887
947
|
- **Template** (`template.rb`): ERB template rendering
|
|
888
948
|
- **Output** (`output.rb`): Output formatting and display
|
|
889
949
|
- **Configuration** (`configuration.rb`): Configuration management
|
|
@@ -893,10 +953,9 @@ Enable verbose output by checking the execution output. Kdeploy provides detaile
|
|
|
893
953
|
|
|
894
954
|
1. **Parse Configuration**: Load and parse `deploy.rb`
|
|
895
955
|
2. **Resolve Hosts**: Determine target hosts based on task definition
|
|
896
|
-
3. **
|
|
897
|
-
4. **
|
|
898
|
-
5. **
|
|
899
|
-
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
|
|
900
959
|
|
|
901
960
|
### Concurrency Model
|
|
902
961
|
|
|
@@ -936,7 +995,6 @@ kdeploy/
|
|
|
936
995
|
│ ├── executor.rb # SSH/SCP executor
|
|
937
996
|
│ ├── runner.rb # Task runner
|
|
938
997
|
│ ├── command_executor.rb # Command executor
|
|
939
|
-
│ ├── command_grouper.rb # Command grouper
|
|
940
998
|
│ ├── template.rb # Template handler
|
|
941
999
|
│ ├── output.rb # Output interface
|
|
942
1000
|
│ ├── configuration.rb # Configuration
|
data/lib/kdeploy/cli.rb
CHANGED
|
@@ -142,21 +142,24 @@ module Kdeploy
|
|
|
142
142
|
print_summary(results, formatter) if show_summary
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
-
def print_host_result(
|
|
145
|
+
def print_host_result(host, result, formatter)
|
|
146
146
|
if %i[success changed].include?(result[:status])
|
|
147
|
-
print_success_result(result, formatter)
|
|
147
|
+
print_success_result(host, result, formatter)
|
|
148
148
|
else
|
|
149
|
-
print_failure_result(result, formatter)
|
|
149
|
+
print_failure_result(host, result, formatter)
|
|
150
150
|
end
|
|
151
|
+
|
|
152
|
+
duration = formatter.calculate_host_duration(result)
|
|
153
|
+
puts "#{formatter.host_prefix(host)}#{formatter.format_host_completed(duration)}" if duration.positive?
|
|
151
154
|
end
|
|
152
155
|
|
|
153
|
-
def print_success_result(result, formatter)
|
|
156
|
+
def print_success_result(host, result, formatter)
|
|
154
157
|
shown = {}
|
|
155
158
|
grouped = group_output_by_type(result[:output])
|
|
156
159
|
|
|
157
160
|
grouped.each do |type, steps|
|
|
158
161
|
output_lines = format_steps_by_type(type, steps, shown, formatter)
|
|
159
|
-
output_lines.each { |line| puts line }
|
|
162
|
+
output_lines.each { |line| puts "#{formatter.host_prefix(host)}#{line}" }
|
|
160
163
|
end
|
|
161
164
|
end
|
|
162
165
|
|
|
@@ -179,9 +182,21 @@ module Kdeploy
|
|
|
179
182
|
end
|
|
180
183
|
end
|
|
181
184
|
|
|
182
|
-
def print_failure_result(result, formatter)
|
|
185
|
+
def print_failure_result(host, result, formatter)
|
|
183
186
|
error_message = extract_error_message(result)
|
|
184
|
-
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
|
|
185
200
|
end
|
|
186
201
|
|
|
187
202
|
def print_summary(results, formatter)
|
|
@@ -214,6 +229,9 @@ module Kdeploy
|
|
|
214
229
|
task_results = execute_single_task(task)
|
|
215
230
|
# Collect results for final summary
|
|
216
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)
|
|
217
235
|
end
|
|
218
236
|
|
|
219
237
|
# Show combined summary at the end for all tasks
|
|
@@ -275,6 +293,12 @@ module Kdeploy
|
|
|
275
293
|
end
|
|
276
294
|
end
|
|
277
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
|
+
|
|
278
302
|
def print_all_tasks_summary(all_results)
|
|
279
303
|
debug_mode = options[:debug] || false
|
|
280
304
|
formatter = OutputFormatter.new(debug: debug_mode)
|
|
@@ -11,26 +11,18 @@ module Kdeploy
|
|
|
11
11
|
@retry_delay = retry_delay.to_f
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def execute_run(command,
|
|
14
|
+
def execute_run(command, _host_name)
|
|
15
15
|
cmd = command[:command]
|
|
16
16
|
use_sudo = command[:sudo]
|
|
17
|
-
show_command_header(host_name, :run, cmd)
|
|
18
|
-
|
|
19
|
-
# Show progress indicator for long-running commands
|
|
20
|
-
pastel = @output.respond_to?(:pastel) ? @output.pastel : Pastel.new
|
|
21
17
|
|
|
22
18
|
result, duration = measure_time do
|
|
23
19
|
with_retries { @executor.execute(cmd, use_sudo: use_sudo) }
|
|
24
20
|
end
|
|
25
21
|
|
|
26
|
-
# Show execution time if command took more than 1 second
|
|
27
|
-
@output.write_line(pastel.dim(" [completed in #{format('%.2f', duration)}s]")) if duration > 1.0
|
|
28
|
-
|
|
29
22
|
{ command: cmd, output: result, duration: duration, type: :run }
|
|
30
23
|
end
|
|
31
24
|
|
|
32
|
-
def execute_upload(command,
|
|
33
|
-
show_command_header(host_name, :upload, "#{command[:source]} -> #{command[:destination]}")
|
|
25
|
+
def execute_upload(command, _host_name)
|
|
34
26
|
_result, duration = measure_time do
|
|
35
27
|
with_retries { @executor.upload(command[:source], command[:destination]) }
|
|
36
28
|
end
|
|
@@ -41,8 +33,7 @@ module Kdeploy
|
|
|
41
33
|
}
|
|
42
34
|
end
|
|
43
35
|
|
|
44
|
-
def execute_upload_template(command,
|
|
45
|
-
show_command_header(host_name, :upload_template, "#{command[:source]} -> #{command[:destination]}")
|
|
36
|
+
def execute_upload_template(command, _host_name)
|
|
46
37
|
_result, duration = measure_time do
|
|
47
38
|
with_retries do
|
|
48
39
|
@executor.upload_template(command[:source], command[:destination], command[:variables])
|
|
@@ -55,11 +46,9 @@ module Kdeploy
|
|
|
55
46
|
}
|
|
56
47
|
end
|
|
57
48
|
|
|
58
|
-
def execute_sync(command,
|
|
49
|
+
def execute_sync(command, _host_name)
|
|
59
50
|
source = command[:source]
|
|
60
51
|
destination = command[:destination]
|
|
61
|
-
description = build_sync_description(source, destination, command[:delete])
|
|
62
|
-
show_command_header(host_name, :sync, description)
|
|
63
52
|
|
|
64
53
|
result, duration = measure_time do
|
|
65
54
|
with_retries do
|
|
@@ -78,12 +67,6 @@ module Kdeploy
|
|
|
78
67
|
|
|
79
68
|
private
|
|
80
69
|
|
|
81
|
-
def build_sync_description(source, destination, delete)
|
|
82
|
-
desc = "sync: #{source} -> #{destination}"
|
|
83
|
-
desc += " (delete: #{delete})" if delete
|
|
84
|
-
desc
|
|
85
|
-
end
|
|
86
|
-
|
|
87
70
|
def build_sync_result(source, destination, result, duration)
|
|
88
71
|
{
|
|
89
72
|
command: "sync: #{source} -> #{destination}",
|
|
@@ -115,32 +98,5 @@ module Kdeploy
|
|
|
115
98
|
retry
|
|
116
99
|
end
|
|
117
100
|
end
|
|
118
|
-
|
|
119
|
-
def show_command_header(host_name, type, description)
|
|
120
|
-
# Don't show command header during execution - it will be shown in results
|
|
121
|
-
# This reduces noise during execution
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def pastel_instance
|
|
125
|
-
@output.respond_to?(:pastel) ? @output.pastel : Pastel.new
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def format_command_by_type(type, description, pastel)
|
|
129
|
-
case type
|
|
130
|
-
when :run
|
|
131
|
-
format_run_command(description, pastel)
|
|
132
|
-
when :upload
|
|
133
|
-
@output.write_line(pastel.green(" [upload] #{description}"))
|
|
134
|
-
when :upload_template
|
|
135
|
-
@output.write_line(pastel.yellow(" [template] #{description}"))
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def format_run_command(description, pastel)
|
|
140
|
-
@output.write_line(pastel.cyan(" [run] #{description.lines.first.strip}"))
|
|
141
|
-
description.lines[1..].each do |line|
|
|
142
|
-
@output.write_line(" > #{line.strip}") unless line.strip.empty?
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
101
|
end
|
|
146
102
|
end
|
data/lib/kdeploy/dsl.rb
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'set'
|
|
4
|
+
require 'shellwords'
|
|
4
5
|
|
|
5
6
|
module Kdeploy
|
|
7
|
+
# Helper for template resource block: captures source and variables
|
|
8
|
+
class TemplateOptions
|
|
9
|
+
def initialize
|
|
10
|
+
@source = nil
|
|
11
|
+
@variables = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def variables(val = nil)
|
|
15
|
+
@variables = val if val
|
|
16
|
+
@variables
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def source(val = nil)
|
|
20
|
+
@source = val if val
|
|
21
|
+
@source
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
6
25
|
# Domain-specific language for defining hosts, roles, and tasks
|
|
7
26
|
module DSL
|
|
8
27
|
def self.included(base)
|
|
@@ -154,6 +173,60 @@ module Kdeploy
|
|
|
154
173
|
}
|
|
155
174
|
end
|
|
156
175
|
|
|
176
|
+
# -------------------------------------------------------------------------
|
|
177
|
+
# Chef-style resource DSL (compiles to run/upload/upload_template)
|
|
178
|
+
# -------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
# 安装系统包。默认 apt 平台;支持 platform: :yum。
|
|
181
|
+
def package(name, version: nil, platform: :apt)
|
|
182
|
+
@kdeploy_commands ||= []
|
|
183
|
+
cmd = build_package_command(name, version, platform)
|
|
184
|
+
@kdeploy_commands << { type: :run, command: cmd, sudo: true }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# 管理系统服务(systemd)。action 支持 :start, :stop, :restart, :reload, :enable, :disable。
|
|
188
|
+
def service(name, action: :start)
|
|
189
|
+
@kdeploy_commands ||= []
|
|
190
|
+
actions = Array(action)
|
|
191
|
+
actions.each do |a|
|
|
192
|
+
cmd = "systemctl #{a} #{Shellwords.escape(name.to_s)}"
|
|
193
|
+
@kdeploy_commands << { type: :run, command: cmd, sudo: true }
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# 部署 ERB 模板到远程路径。支持 block 或关键字参数。
|
|
198
|
+
def template(destination, source: nil, variables: nil, &block)
|
|
199
|
+
@kdeploy_commands ||= []
|
|
200
|
+
if block
|
|
201
|
+
opts = TemplateOptions.new
|
|
202
|
+
opts.instance_eval(&block)
|
|
203
|
+
src = opts.source || raise(ArgumentError, 'template requires source')
|
|
204
|
+
vars = opts.variables || {}
|
|
205
|
+
else
|
|
206
|
+
raise ArgumentError, 'template requires source' unless source
|
|
207
|
+
|
|
208
|
+
src = source
|
|
209
|
+
vars = variables || {}
|
|
210
|
+
end
|
|
211
|
+
upload_template(src, destination, vars)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# 上传本地文件到远程路径。
|
|
215
|
+
def file(destination, source:)
|
|
216
|
+
@kdeploy_commands ||= []
|
|
217
|
+
upload(source, destination)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# 确保远程目录存在。支持 mode 参数。
|
|
221
|
+
def directory(path, mode: nil)
|
|
222
|
+
@kdeploy_commands ||= []
|
|
223
|
+
cmd = "mkdir -p #{Shellwords.escape(path.to_s)}"
|
|
224
|
+
@kdeploy_commands << { type: :run, command: cmd, sudo: true }
|
|
225
|
+
return unless mode
|
|
226
|
+
|
|
227
|
+
@kdeploy_commands << { type: :run, command: "chmod #{mode} #{Shellwords.escape(path.to_s)}", sudo: true }
|
|
228
|
+
end
|
|
229
|
+
|
|
157
230
|
def inventory(&block)
|
|
158
231
|
instance_eval(&block) if block_given?
|
|
159
232
|
end
|
|
@@ -188,5 +261,20 @@ module Kdeploy
|
|
|
188
261
|
end
|
|
189
262
|
end
|
|
190
263
|
end
|
|
264
|
+
|
|
265
|
+
def build_package_command(name, version, platform)
|
|
266
|
+
n = Shellwords.escape(name.to_s)
|
|
267
|
+
case platform.to_sym
|
|
268
|
+
when :yum, :rpm
|
|
269
|
+
if version
|
|
270
|
+
"yum install -y #{n}-#{Shellwords.escape(version.to_s)}"
|
|
271
|
+
else
|
|
272
|
+
"yum install -y #{n}"
|
|
273
|
+
end
|
|
274
|
+
else
|
|
275
|
+
base = "apt-get update && apt-get install -y #{n}"
|
|
276
|
+
version ? "#{base}=#{Shellwords.escape(version.to_s)}" : base
|
|
277
|
+
end
|
|
278
|
+
end
|
|
191
279
|
end
|
|
192
280
|
end
|
data/lib/kdeploy/errors.rb
CHANGED
|
@@ -20,12 +20,16 @@ module Kdeploy
|
|
|
20
20
|
|
|
21
21
|
# Raised when SSH operation fails
|
|
22
22
|
class SSHError < Error
|
|
23
|
-
def initialize(message, original_error = nil)
|
|
23
|
+
def initialize(message, original_error = nil, command: nil, exit_status: nil, stdout: nil, stderr: nil)
|
|
24
24
|
super("SSH operation failed: #{message}")
|
|
25
25
|
@original_error = original_error
|
|
26
|
+
@command = command
|
|
27
|
+
@exit_status = exit_status
|
|
28
|
+
@stdout = stdout
|
|
29
|
+
@stderr = stderr
|
|
26
30
|
end
|
|
27
31
|
|
|
28
|
-
attr_reader :original_error
|
|
32
|
+
attr_reader :original_error, :command, :exit_status, :stdout, :stderr
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
# Raised when SCP operation fails
|
data/lib/kdeploy/executor.rb
CHANGED
|
@@ -38,16 +38,21 @@ module Kdeploy
|
|
|
38
38
|
def execute_command_on_ssh(ssh, command)
|
|
39
39
|
stdout = String.new
|
|
40
40
|
stderr = String.new
|
|
41
|
+
exit_status = nil
|
|
41
42
|
|
|
42
43
|
ssh.open_channel do |channel|
|
|
43
44
|
channel.exec(command) do |_ch, success|
|
|
44
45
|
raise SSHError, "Could not execute command: #{command}" unless success
|
|
45
46
|
|
|
46
47
|
setup_channel_handlers(channel, stdout, stderr)
|
|
48
|
+
channel.on_request('exit-status') do |_ch, data|
|
|
49
|
+
exit_status = data.read_long
|
|
50
|
+
end
|
|
47
51
|
end
|
|
48
52
|
end
|
|
49
53
|
ssh.loop
|
|
50
|
-
|
|
54
|
+
raise_nonzero_exit!(command, exit_status, stdout, stderr)
|
|
55
|
+
build_command_result(stdout, stderr, command, exit_status)
|
|
51
56
|
end
|
|
52
57
|
|
|
53
58
|
def setup_channel_handlers(channel, stdout, stderr)
|
|
@@ -60,14 +65,29 @@ module Kdeploy
|
|
|
60
65
|
end
|
|
61
66
|
end
|
|
62
67
|
|
|
63
|
-
def build_command_result(stdout, stderr, command)
|
|
68
|
+
def build_command_result(stdout, stderr, command, exit_status)
|
|
64
69
|
{
|
|
65
70
|
stdout: stdout.strip,
|
|
66
71
|
stderr: stderr.strip,
|
|
67
|
-
command: command
|
|
72
|
+
command: command,
|
|
73
|
+
exit_status: exit_status
|
|
68
74
|
}
|
|
69
75
|
end
|
|
70
76
|
|
|
77
|
+
def raise_nonzero_exit!(command, exit_status, stdout, stderr)
|
|
78
|
+
return if exit_status.nil?
|
|
79
|
+
return if exit_status.zero?
|
|
80
|
+
|
|
81
|
+
raise SSHError.new(
|
|
82
|
+
"Command exited with status #{exit_status}",
|
|
83
|
+
nil,
|
|
84
|
+
command: command,
|
|
85
|
+
exit_status: exit_status,
|
|
86
|
+
stdout: stdout.strip,
|
|
87
|
+
stderr: stderr.strip
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
71
91
|
def upload(source, destination, use_sudo: nil)
|
|
72
92
|
use_sudo = @use_sudo if use_sudo.nil?
|
|
73
93
|
|
data/lib/kdeploy/initializer.rb
CHANGED
|
@@ -39,27 +39,21 @@ module Kdeploy
|
|
|
39
39
|
role :web, %w[web01 web02]
|
|
40
40
|
role :db, %w[db01]
|
|
41
41
|
|
|
42
|
-
# Define deployment task for web servers
|
|
42
|
+
# Define deployment task for web servers (Chef-style resource DSL)
|
|
43
43
|
task :deploy_web, roles: :web do
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
upload './config/app.conf', '/etc/nginx/conf.d/app.conf'
|
|
58
|
-
|
|
59
|
-
run <<~SHELL
|
|
60
|
-
sudo systemctl start nginx
|
|
61
|
-
sudo systemctl status nginx
|
|
62
|
-
SHELL
|
|
44
|
+
package 'nginx'
|
|
45
|
+
directory '/etc/nginx/conf.d'
|
|
46
|
+
template '/etc/nginx/nginx.conf',
|
|
47
|
+
source: './config/nginx.conf.erb',
|
|
48
|
+
variables: {
|
|
49
|
+
domain_name: 'example.com',
|
|
50
|
+
port: 3000,
|
|
51
|
+
worker_processes: 4,
|
|
52
|
+
worker_connections: 2048
|
|
53
|
+
}
|
|
54
|
+
file '/etc/nginx/conf.d/app.conf', source: './config/app.conf'
|
|
55
|
+
run 'nginx -t', sudo: true
|
|
56
|
+
service 'nginx', action: %i[enable restart]
|
|
63
57
|
end
|
|
64
58
|
|
|
65
59
|
# Define backup task for database servers
|
|
@@ -71,13 +65,11 @@ module Kdeploy
|
|
|
71
65
|
SHELL
|
|
72
66
|
end
|
|
73
67
|
|
|
74
|
-
# Define task for specific hosts
|
|
68
|
+
# Define task for specific hosts (resource DSL example)
|
|
75
69
|
task :maintenance, on: %w[web01] do
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
sudo systemctl start nginx
|
|
80
|
-
SHELL
|
|
70
|
+
service 'nginx', action: :stop
|
|
71
|
+
run 'apt-get update && apt-get upgrade -y', sudo: true
|
|
72
|
+
service 'nginx', action: %i[start enable]
|
|
81
73
|
end
|
|
82
74
|
|
|
83
75
|
# Define task for all hosts
|
|
@@ -199,12 +191,12 @@ module Kdeploy
|
|
|
199
191
|
server_name <%= domain_name %>;
|
|
200
192
|
```
|
|
201
193
|
|
|
202
|
-
Variables are passed when uploading the template:
|
|
194
|
+
Variables are passed when uploading the template (Chef-style resource DSL):
|
|
203
195
|
|
|
204
196
|
```ruby
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
worker_processes: 4
|
|
197
|
+
template "/etc/nginx/nginx.conf",
|
|
198
|
+
source: "./config/nginx.conf.erb",
|
|
199
|
+
variables: { domain_name: "example.com", worker_processes: 4 }
|
|
208
200
|
```
|
|
209
201
|
|
|
210
202
|
## 🔐 Using Sudo
|
|
@@ -24,64 +24,60 @@ module Kdeploy
|
|
|
24
24
|
@pastel.bright_white(" #{host.ljust(20)} #{status_str}")
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
# Prefix for per-step lines to make multi-host logs easier to scan.
|
|
28
|
+
def host_prefix(host)
|
|
29
|
+
@pastel.dim(" #{host.ljust(20)} ")
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
def
|
|
32
|
-
|
|
32
|
+
def format_host_completed(duration)
|
|
33
|
+
@pastel.dim(" [completed in #{format('%.2f', duration)}s]")
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
steps.each do |step|
|
|
38
|
-
next if step_already_shown?(step, :sync, shown)
|
|
36
|
+
def calculate_host_duration(result)
|
|
37
|
+
return 0.0 unless result.is_a?(Hash)
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
output << format_sync_step(step)
|
|
42
|
-
end
|
|
43
|
-
output
|
|
39
|
+
Array(result[:output]).sum { |step| step[:duration].to_f }
|
|
44
40
|
end
|
|
45
41
|
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
next if step_already_shown?(step, type, shown)
|
|
42
|
+
def format_upload_steps(steps, _shown = nil)
|
|
43
|
+
format_file_steps(steps, :upload, 'upload: ')
|
|
44
|
+
end
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
def format_template_steps(steps, _shown = nil)
|
|
47
|
+
format_file_steps(steps, :upload_template, 'upload_template: ')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def format_sync_steps(steps, _shown = nil)
|
|
51
|
+
steps.map { |step| format_sync_step(step) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def format_file_steps(steps, type, prefix)
|
|
55
|
+
steps.map { |step| format_file_step(step, type, prefix) }
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
def format_file_step(step, type, prefix)
|
|
58
59
|
duration_str = format_duration(step[:duration])
|
|
60
|
+
status_str = format_step_status(step)
|
|
59
61
|
icon = type == :upload ? '📤' : '📝'
|
|
60
62
|
file_path = step[:command].sub(prefix, '')
|
|
61
63
|
# Truncate long paths for cleaner output
|
|
62
64
|
display_path = file_path.length > 50 ? "...#{file_path[-47..]}" : file_path
|
|
63
65
|
color_method = type == :upload ? :green : :yellow
|
|
64
|
-
@pastel.dim(" #{icon} ") + @pastel.send(color_method, display_path) + duration_str
|
|
66
|
+
@pastel.dim(" #{icon} ") + @pastel.send(color_method, display_path) + duration_str + " #{status_str}"
|
|
65
67
|
end
|
|
66
68
|
|
|
67
|
-
def format_run_steps(steps,
|
|
68
|
-
|
|
69
|
-
steps.each do |step|
|
|
70
|
-
next if step_already_shown?(step, :run, shown)
|
|
71
|
-
|
|
72
|
-
mark_step_as_shown(step, :run, shown)
|
|
73
|
-
output.concat(format_single_run_step(step))
|
|
74
|
-
end
|
|
75
|
-
output
|
|
69
|
+
def format_run_steps(steps, _shown = nil)
|
|
70
|
+
steps.flat_map { |step| format_single_run_step(step) }
|
|
76
71
|
end
|
|
77
72
|
|
|
78
73
|
def format_single_run_step(step)
|
|
79
74
|
output = []
|
|
80
75
|
duration_str = format_duration(step[:duration])
|
|
81
|
-
|
|
76
|
+
status_str = format_step_status(step)
|
|
77
|
+
command_line = first_meaningful_command_line(step[:command].to_s)
|
|
82
78
|
# Truncate long commands for cleaner output
|
|
83
79
|
display_cmd = command_line.length > 60 ? "#{command_line[0..57]}..." : command_line
|
|
84
|
-
output << (@pastel.dim(' • ') + @pastel.cyan(display_cmd) + duration_str)
|
|
80
|
+
output << (@pastel.dim(' • ') + @pastel.cyan(display_cmd) + duration_str + " #{status_str}")
|
|
85
81
|
# Only show multiline details in debug mode
|
|
86
82
|
if @debug
|
|
87
83
|
output.concat(format_multiline_command(step[:command]))
|
|
@@ -187,6 +183,17 @@ module Kdeploy
|
|
|
187
183
|
output
|
|
188
184
|
end
|
|
189
185
|
|
|
186
|
+
def first_meaningful_command_line(command)
|
|
187
|
+
lines = command.to_s.lines.map(&:strip)
|
|
188
|
+
lines.each do |line|
|
|
189
|
+
next if line.empty?
|
|
190
|
+
next if line.start_with?('#')
|
|
191
|
+
|
|
192
|
+
return line
|
|
193
|
+
end
|
|
194
|
+
lines.first.to_s
|
|
195
|
+
end
|
|
196
|
+
|
|
190
197
|
def format_command_output(output)
|
|
191
198
|
result = []
|
|
192
199
|
return result unless output
|
|
@@ -240,18 +247,9 @@ module Kdeploy
|
|
|
240
247
|
result
|
|
241
248
|
end
|
|
242
249
|
|
|
243
|
-
def step_already_shown?(step, type, shown)
|
|
244
|
-
key = [step[:command], type].hash
|
|
245
|
-
shown[key]
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def mark_step_as_shown(step, type, shown)
|
|
249
|
-
key = [step[:command], type].hash
|
|
250
|
-
shown[key] = true
|
|
251
|
-
end
|
|
252
|
-
|
|
253
250
|
def format_sync_step(step)
|
|
254
251
|
duration_str = format_duration(step[:duration])
|
|
252
|
+
status_str = format_step_status(step)
|
|
255
253
|
sync_path = step[:command].sub('sync: ', '')
|
|
256
254
|
# Truncate long paths for cleaner output
|
|
257
255
|
display_path = sync_path.length > 50 ? "...#{sync_path[-47..]}" : sync_path
|
|
@@ -266,7 +264,15 @@ module Kdeploy
|
|
|
266
264
|
stats << @pastel.yellow("#{deleted} deleted") if deleted.positive?
|
|
267
265
|
stats_str = stats.any? ? " (#{stats.join(', ')})" : " (#{total} files)"
|
|
268
266
|
|
|
269
|
-
@pastel.dim(' 📁 ') + @pastel.cyan(display_path) + @pastel.dim(stats_str) + duration_str
|
|
267
|
+
@pastel.dim(' 📁 ') + @pastel.cyan(display_path) + @pastel.dim(stats_str) + duration_str + " #{status_str}"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def format_step_status(step)
|
|
271
|
+
if step.is_a?(Hash) && step.key?(:error) && step[:error] && !step[:error].to_s.empty?
|
|
272
|
+
@pastel.red('✗ failed')
|
|
273
|
+
else
|
|
274
|
+
@pastel.green('✓ ok')
|
|
275
|
+
end
|
|
270
276
|
end
|
|
271
277
|
end
|
|
272
278
|
end
|
data/lib/kdeploy/runner.rb
CHANGED
|
@@ -41,10 +41,6 @@ module Kdeploy
|
|
|
41
41
|
# If no hosts, return empty results immediately
|
|
42
42
|
return @results if futures.empty?
|
|
43
43
|
|
|
44
|
-
# Show progress while waiting for tasks to complete
|
|
45
|
-
total = futures.length
|
|
46
|
-
completed = 0
|
|
47
|
-
|
|
48
44
|
# Collect results from futures
|
|
49
45
|
futures.each_with_index do |future, index|
|
|
50
46
|
host_name = @host_names[index] # Get host name from the stored list
|
|
@@ -86,14 +82,6 @@ module Kdeploy
|
|
|
86
82
|
# Ensure we always have a result for this host
|
|
87
83
|
@results[host_name] ||= { status: :unknown, error: 'No result collected', output: [] }
|
|
88
84
|
end
|
|
89
|
-
|
|
90
|
-
completed += 1
|
|
91
|
-
# Show progress for multiple hosts
|
|
92
|
-
next unless total > 1
|
|
93
|
-
|
|
94
|
-
pastel = @output.respond_to?(:pastel) ? @output.pastel : Pastel.new
|
|
95
|
-
@output.write_line(pastel.dim(" [Progress: #{completed}/#{total} hosts completed]"))
|
|
96
|
-
@output.flush if @output.respond_to?(:flush)
|
|
97
85
|
end
|
|
98
86
|
|
|
99
87
|
@results
|
|
@@ -125,41 +113,47 @@ module Kdeploy
|
|
|
125
113
|
result = { status: :success, output: [] }
|
|
126
114
|
|
|
127
115
|
begin
|
|
128
|
-
|
|
116
|
+
execute_commands(task, command_executor, name, result, task_name)
|
|
129
117
|
rescue StandardError => e
|
|
130
|
-
#
|
|
131
|
-
|
|
132
|
-
result =
|
|
118
|
+
# Keep any already collected step output for troubleshooting.
|
|
119
|
+
result[:status] = :failed
|
|
120
|
+
result[:error] = "#{e.class}: #{e.message}"
|
|
133
121
|
end
|
|
134
122
|
|
|
135
123
|
# Return the result so it can be collected from the future
|
|
136
124
|
[name, result]
|
|
137
125
|
end
|
|
138
126
|
|
|
139
|
-
def
|
|
127
|
+
def execute_commands(task, command_executor, name, result, task_name)
|
|
140
128
|
commands = task[:block].call
|
|
141
|
-
grouped_commands = CommandGrouper.group(commands)
|
|
142
129
|
|
|
143
|
-
|
|
144
|
-
|
|
130
|
+
commands.each do |command|
|
|
131
|
+
step_result = execute_command(command_executor, command, name)
|
|
132
|
+
result[:output] << step_result
|
|
133
|
+
rescue StandardError => e
|
|
134
|
+
step = step_description(command)
|
|
135
|
+
result[:status] = :failed
|
|
136
|
+
result[:error] = "task=#{task_name} host=#{name} step=#{step} error=#{e.class}: #{e.message}"
|
|
137
|
+
result[:output] << {
|
|
138
|
+
type: command[:type],
|
|
139
|
+
command: step_command_string(command),
|
|
140
|
+
duration: 0.0,
|
|
141
|
+
error: "#{e.class}: #{e.message}",
|
|
142
|
+
output: error_output_for_step(e)
|
|
143
|
+
}
|
|
144
|
+
break
|
|
145
145
|
end
|
|
146
146
|
end
|
|
147
147
|
|
|
148
|
-
def
|
|
149
|
-
|
|
150
|
-
task_desc = CommandGrouper.task_description(first_cmd)
|
|
151
|
-
show_task_header(task_desc)
|
|
148
|
+
def error_output_for_step(error)
|
|
149
|
+
return nil unless error.is_a?(Kdeploy::SSHError)
|
|
152
150
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
step_result = execute_command_with_context(command_executor, command, name, task_name)
|
|
161
|
-
result[:output] << step_result
|
|
162
|
-
end
|
|
151
|
+
{
|
|
152
|
+
stdout: error.stdout,
|
|
153
|
+
stderr: error.stderr,
|
|
154
|
+
exit_status: error.exit_status,
|
|
155
|
+
command: error.command
|
|
156
|
+
}
|
|
163
157
|
end
|
|
164
158
|
|
|
165
159
|
def execute_command(command_executor, command, host_name)
|
|
@@ -177,11 +171,19 @@ module Kdeploy
|
|
|
177
171
|
end
|
|
178
172
|
end
|
|
179
173
|
|
|
180
|
-
def
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
174
|
+
def step_command_string(command)
|
|
175
|
+
case command[:type]
|
|
176
|
+
when :run
|
|
177
|
+
command[:command].to_s
|
|
178
|
+
when :upload
|
|
179
|
+
"upload: #{command[:source]} -> #{command[:destination]}"
|
|
180
|
+
when :upload_template
|
|
181
|
+
"upload_template: #{command[:source]} -> #{command[:destination]}"
|
|
182
|
+
when :sync
|
|
183
|
+
"sync: #{command[:source]} -> #{command[:destination]}"
|
|
184
|
+
else
|
|
185
|
+
command[:type].to_s
|
|
186
|
+
end
|
|
185
187
|
end
|
|
186
188
|
|
|
187
189
|
def step_description(command)
|
|
@@ -199,10 +201,5 @@ module Kdeploy
|
|
|
199
201
|
command[:type].to_s
|
|
200
202
|
end
|
|
201
203
|
end
|
|
202
|
-
|
|
203
|
-
def show_task_header(task_desc)
|
|
204
|
-
# Don't show command header during execution - it will be shown in results
|
|
205
|
-
# This reduces noise during execution
|
|
206
|
-
end
|
|
207
204
|
end
|
|
208
205
|
end
|
data/lib/kdeploy/version.rb
CHANGED
data/lib/kdeploy.rb
CHANGED
|
@@ -8,7 +8,6 @@ require_relative 'kdeploy/banner'
|
|
|
8
8
|
require_relative 'kdeploy/file_filter'
|
|
9
9
|
require_relative 'kdeploy/dsl'
|
|
10
10
|
require_relative 'kdeploy/executor'
|
|
11
|
-
require_relative 'kdeploy/command_grouper'
|
|
12
11
|
require_relative 'kdeploy/command_executor'
|
|
13
12
|
require_relative 'kdeploy/output_formatter'
|
|
14
13
|
require_relative 'kdeploy/help_formatter'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kdeploy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.38
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kk
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-02-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bcrypt_pbkdf
|
|
@@ -169,7 +169,6 @@ files:
|
|
|
169
169
|
- lib/kdeploy/banner.rb
|
|
170
170
|
- lib/kdeploy/cli.rb
|
|
171
171
|
- lib/kdeploy/command_executor.rb
|
|
172
|
-
- lib/kdeploy/command_grouper.rb
|
|
173
172
|
- lib/kdeploy/completions/kdeploy.bash
|
|
174
173
|
- lib/kdeploy/completions/kdeploy.zsh
|
|
175
174
|
- lib/kdeploy/configuration.rb
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Kdeploy
|
|
4
|
-
# Groups commands by type and generates task descriptions
|
|
5
|
-
class CommandGrouper
|
|
6
|
-
def self.group(commands)
|
|
7
|
-
commands.group_by do |cmd|
|
|
8
|
-
group_key_for(cmd)
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def self.group_key_for(cmd)
|
|
13
|
-
case cmd[:type]
|
|
14
|
-
when :upload, :upload_template, :sync
|
|
15
|
-
"#{cmd[:type]}_#{cmd[:source]}"
|
|
16
|
-
when :run
|
|
17
|
-
"#{cmd[:type]}_#{cmd[:command].to_s.lines.first.strip}"
|
|
18
|
-
else
|
|
19
|
-
cmd[:type].to_s
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def self.task_description(command)
|
|
24
|
-
case command[:type]
|
|
25
|
-
when :upload
|
|
26
|
-
"upload #{command[:source]}"
|
|
27
|
-
when :upload_template
|
|
28
|
-
"template #{command[:source]}"
|
|
29
|
-
when :sync
|
|
30
|
-
"sync #{command[:source]}"
|
|
31
|
-
when :run
|
|
32
|
-
command[:command].to_s.lines.first.strip
|
|
33
|
-
else
|
|
34
|
-
command[:type].to_s
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|