kdeploy 1.2.33 → 1.2.39
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 +121 -79
- data/README_EN.md +131 -72
- 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 +28 -6
- 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
data/README_EN.md
CHANGED
|
@@ -97,6 +97,12 @@ kdeploy version
|
|
|
97
97
|
|
|
98
98
|
You should see the version information and banner.
|
|
99
99
|
|
|
100
|
+
**If `kdeploy` is not found**: the gem executable directory may not be in your PATH. Add to `~/.zshrc` or `~/.bashrc` then run `source ~/.zshrc`:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
export PATH="$(ruby -e 'puts Gem.bindir'):$PATH"
|
|
104
|
+
```
|
|
105
|
+
|
|
100
106
|
### Shell Completion
|
|
101
107
|
|
|
102
108
|
Kdeploy automatically configures shell completion during installation. If needed, manually add to your shell config:
|
|
@@ -136,35 +142,30 @@ This creates a new directory with:
|
|
|
136
142
|
|
|
137
143
|
### 2. Configure Hosts and Tasks
|
|
138
144
|
|
|
139
|
-
Edit `deploy.rb
|
|
145
|
+
Edit `deploy.rb` (using Chef-style resource DSL):
|
|
140
146
|
|
|
141
147
|
```ruby
|
|
142
148
|
# Define hosts
|
|
143
149
|
host "web01", user: "ubuntu", ip: "10.0.0.1", key: "~/.ssh/id_rsa"
|
|
144
150
|
host "web02", user: "ubuntu", ip: "10.0.0.2", key: "~/.ssh/id_rsa"
|
|
145
|
-
|
|
146
|
-
# Define roles
|
|
147
151
|
role :web, %w[web01 web02]
|
|
148
152
|
|
|
149
153
|
# Define deployment task
|
|
150
|
-
task :
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
port: 3000
|
|
159
|
-
|
|
160
|
-
run "sudo systemctl start nginx"
|
|
154
|
+
task :deploy_web, roles: :web do
|
|
155
|
+
package "nginx"
|
|
156
|
+
directory "/etc/nginx/conf.d"
|
|
157
|
+
template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb",
|
|
158
|
+
variables: { domain_name: "example.com", port: 3000 }
|
|
159
|
+
file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
|
|
160
|
+
run "nginx -t", sudo: true
|
|
161
|
+
service "nginx", action: %i[enable restart]
|
|
161
162
|
end
|
|
162
163
|
```
|
|
163
164
|
|
|
164
165
|
### 3. Run Deployment
|
|
165
166
|
|
|
166
167
|
```bash
|
|
167
|
-
kdeploy execute deploy.rb
|
|
168
|
+
kdeploy execute deploy.rb deploy_web
|
|
168
169
|
```
|
|
169
170
|
|
|
170
171
|
## 📖 Usage Guide
|
|
@@ -355,7 +356,7 @@ end
|
|
|
355
356
|
|
|
356
357
|
```ruby
|
|
357
358
|
task :deploy_web, roles: :web do
|
|
358
|
-
|
|
359
|
+
service "nginx", action: :restart
|
|
359
360
|
end
|
|
360
361
|
```
|
|
361
362
|
|
|
@@ -363,29 +364,22 @@ end
|
|
|
363
364
|
|
|
364
365
|
```ruby
|
|
365
366
|
task :maintenance, on: %w[web01] do
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
sudo systemctl start nginx
|
|
370
|
-
SHELL
|
|
367
|
+
service "nginx", action: :stop
|
|
368
|
+
run "apt-get update && apt-get upgrade -y", sudo: true
|
|
369
|
+
service "nginx", action: %i[start enable]
|
|
371
370
|
end
|
|
372
371
|
```
|
|
373
372
|
|
|
374
373
|
#### Task with Multiple Commands
|
|
375
374
|
|
|
376
375
|
```ruby
|
|
377
|
-
task :
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
# Start service
|
|
385
|
-
run "sudo systemctl start nginx"
|
|
386
|
-
|
|
387
|
-
# Verify status
|
|
388
|
-
run "sudo systemctl status nginx"
|
|
376
|
+
task :deploy_web, roles: :web do
|
|
377
|
+
package "nginx"
|
|
378
|
+
directory "/etc/nginx/conf.d"
|
|
379
|
+
template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
|
|
380
|
+
file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
|
|
381
|
+
run "nginx -t", sudo: true
|
|
382
|
+
service "nginx", action: %i[enable restart]
|
|
389
383
|
end
|
|
390
384
|
```
|
|
391
385
|
|
|
@@ -486,6 +480,67 @@ upload_template "./config/nginx.conf.erb", "/etc/nginx/nginx.conf",
|
|
|
486
480
|
- `destination`: Remote file path
|
|
487
481
|
- `variables`: Hash of variables for template rendering
|
|
488
482
|
|
|
483
|
+
### Chef-Style Resource DSL
|
|
484
|
+
|
|
485
|
+
Kdeploy provides a declarative resource DSL similar to Chef, which can replace or mix with low-level primitives (`run`, `upload`, `upload_template`).
|
|
486
|
+
|
|
487
|
+
#### `package` - Install System Packages
|
|
488
|
+
|
|
489
|
+
```ruby
|
|
490
|
+
package "nginx"
|
|
491
|
+
package "nginx", version: "1.18"
|
|
492
|
+
package "nginx", platform: :yum # CentOS/RHEL
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Uses apt (Ubuntu/Debian) by default; `platform: :yum` generates yum commands.
|
|
496
|
+
|
|
497
|
+
#### `service` - Manage System Services (systemd)
|
|
498
|
+
|
|
499
|
+
```ruby
|
|
500
|
+
service "nginx", action: [:enable, :start]
|
|
501
|
+
service "nginx", action: :restart
|
|
502
|
+
service "nginx", action: [:stop, :disable]
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Supports `:start`, `:stop`, `:restart`, `:reload`, `:enable`, `:disable`.
|
|
506
|
+
|
|
507
|
+
#### `template` - Deploy ERB Templates
|
|
508
|
+
|
|
509
|
+
```ruby
|
|
510
|
+
template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
|
|
511
|
+
# Or block syntax
|
|
512
|
+
template "/etc/app.conf" do
|
|
513
|
+
source "./config/app.erb"
|
|
514
|
+
variables(domain: "example.com")
|
|
515
|
+
end
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
#### `file` - Upload Local Files
|
|
519
|
+
|
|
520
|
+
```ruby
|
|
521
|
+
file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
#### `directory` - Ensure Remote Directory Exists
|
|
525
|
+
|
|
526
|
+
```ruby
|
|
527
|
+
directory "/etc/nginx/conf.d"
|
|
528
|
+
directory "/var/log/app", mode: "0755"
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**Example: Deploy Nginx Using Resource DSL**
|
|
532
|
+
|
|
533
|
+
```ruby
|
|
534
|
+
task :deploy_nginx, roles: :web do
|
|
535
|
+
package "nginx"
|
|
536
|
+
directory "/etc/nginx/conf.d"
|
|
537
|
+
template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
|
|
538
|
+
file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
|
|
539
|
+
run "nginx -t", sudo: true
|
|
540
|
+
service "nginx", action: %i[enable restart]
|
|
541
|
+
end
|
|
542
|
+
```
|
|
543
|
+
|
|
489
544
|
### Template Support
|
|
490
545
|
|
|
491
546
|
Kdeploy supports ERB (Embedded Ruby) templates for dynamic configuration generation.
|
|
@@ -532,11 +587,8 @@ http {
|
|
|
532
587
|
|
|
533
588
|
```ruby
|
|
534
589
|
task :deploy_config do
|
|
535
|
-
|
|
536
|
-
domain_name: "example.com",
|
|
537
|
-
port: 3000,
|
|
538
|
-
worker_processes: 4,
|
|
539
|
-
worker_connections: 2048
|
|
590
|
+
template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb",
|
|
591
|
+
variables: { domain_name: "example.com", port: 3000, worker_processes: 4, worker_connections: 2048 }
|
|
540
592
|
end
|
|
541
593
|
```
|
|
542
594
|
|
|
@@ -597,15 +649,11 @@ Use Ruby conditionals in your deployment files:
|
|
|
597
649
|
|
|
598
650
|
```ruby
|
|
599
651
|
task :deploy do
|
|
600
|
-
if ENV['ENVIRONMENT'] == 'production'
|
|
601
|
-
run "sudo systemctl stop nginx"
|
|
602
|
-
end
|
|
652
|
+
service "nginx", action: :stop if ENV['ENVIRONMENT'] == 'production'
|
|
603
653
|
|
|
604
|
-
|
|
654
|
+
file "/etc/nginx/nginx.conf", source: "./config/nginx.conf"
|
|
605
655
|
|
|
606
|
-
if ENV['ENVIRONMENT'] == 'production'
|
|
607
|
-
run "sudo systemctl start nginx"
|
|
608
|
-
end
|
|
656
|
+
service "nginx", action: :start if ENV['ENVIRONMENT'] == 'production'
|
|
609
657
|
end
|
|
610
658
|
```
|
|
611
659
|
|
|
@@ -628,9 +676,10 @@ end
|
|
|
628
676
|
|
|
629
677
|
```ruby
|
|
630
678
|
task :deploy do
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
run "
|
|
679
|
+
service "nginx", action: :stop
|
|
680
|
+
file "/etc/nginx/nginx.conf", source: "./config/nginx.conf"
|
|
681
|
+
run "nginx -t", sudo: true # run raises on invalid config
|
|
682
|
+
service "nginx", action: :start
|
|
634
683
|
end
|
|
635
684
|
```
|
|
636
685
|
|
|
@@ -746,10 +795,9 @@ end
|
|
|
746
795
|
### 3. Use Templates for Dynamic Configuration
|
|
747
796
|
|
|
748
797
|
```ruby
|
|
749
|
-
# ✅ Good - Use
|
|
750
|
-
|
|
751
|
-
domain_name: "example.com",
|
|
752
|
-
port: 3000
|
|
798
|
+
# ✅ Good - Use template resource
|
|
799
|
+
template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb",
|
|
800
|
+
variables: { domain_name: "example.com", port: 3000 }
|
|
753
801
|
|
|
754
802
|
# ❌ Avoid - Hardcoding values
|
|
755
803
|
run "echo 'server_name example.com;' > /etc/nginx/nginx.conf"
|
|
@@ -759,12 +807,10 @@ run "echo 'server_name example.com;' > /etc/nginx/nginx.conf"
|
|
|
759
807
|
|
|
760
808
|
```ruby
|
|
761
809
|
task :deploy do
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
upload "./config/nginx.conf", "/etc/nginx/nginx.conf"
|
|
767
|
-
run "sudo systemctl reload nginx"
|
|
810
|
+
template "/etc/nginx/nginx.conf", source: "./config/nginx.conf.erb", variables: { port: 3000 }
|
|
811
|
+
file "/etc/nginx/conf.d/app.conf", source: "./config/app.conf"
|
|
812
|
+
run "nginx -t", sudo: true # run raises on invalid config
|
|
813
|
+
service "nginx", action: :reload
|
|
768
814
|
end
|
|
769
815
|
```
|
|
770
816
|
|
|
@@ -883,7 +929,6 @@ Enable verbose output by checking the execution output. Kdeploy provides detaile
|
|
|
883
929
|
- **Executor** (`executor.rb`): SSH/SCP execution engine
|
|
884
930
|
- **Runner** (`runner.rb`): Concurrent task execution coordinator
|
|
885
931
|
- **CommandExecutor** (`command_executor.rb`): Individual command execution
|
|
886
|
-
- **CommandGrouper** (`command_grouper.rb`): Command grouping logic
|
|
887
932
|
- **Template** (`template.rb`): ERB template rendering
|
|
888
933
|
- **Output** (`output.rb`): Output formatting and display
|
|
889
934
|
- **Configuration** (`configuration.rb`): Configuration management
|
|
@@ -893,10 +938,9 @@ Enable verbose output by checking the execution output. Kdeploy provides detaile
|
|
|
893
938
|
|
|
894
939
|
1. **Parse Configuration**: Load and parse `deploy.rb`
|
|
895
940
|
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
|
|
941
|
+
3. **Execute Concurrently**: Run tasks in parallel across hosts, executing commands in order per host
|
|
942
|
+
4. **Collect Results**: Gather execution results and status
|
|
943
|
+
5. **Display Output**: Format and display results to user
|
|
900
944
|
|
|
901
945
|
### Concurrency Model
|
|
902
946
|
|
|
@@ -936,7 +980,6 @@ kdeploy/
|
|
|
936
980
|
│ ├── executor.rb # SSH/SCP executor
|
|
937
981
|
│ ├── runner.rb # Task runner
|
|
938
982
|
│ ├── command_executor.rb # Command executor
|
|
939
|
-
│ ├── command_grouper.rb # Command grouper
|
|
940
983
|
│ ├── template.rb # Template handler
|
|
941
984
|
│ ├── output.rb # Output interface
|
|
942
985
|
│ ├── configuration.rb # Configuration
|
|
@@ -1018,9 +1061,16 @@ Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
|
|
1018
1061
|
|
|
1019
1062
|
## 📚 Examples
|
|
1020
1063
|
|
|
1021
|
-
### Example
|
|
1064
|
+
### Example Project
|
|
1022
1065
|
|
|
1023
|
-
|
|
1066
|
+
The [sample/](sample/) directory in this repo provides a complete example with Nginx, Node Exporter, directory sync tasks, and Vagrant support for local testing:
|
|
1067
|
+
|
|
1068
|
+
```bash
|
|
1069
|
+
cd sample
|
|
1070
|
+
vagrant up
|
|
1071
|
+
kdeploy execute deploy.rb deploy_web --dry-run # Preview
|
|
1072
|
+
kdeploy execute deploy.rb deploy_web # Execute
|
|
1073
|
+
```
|
|
1024
1074
|
|
|
1025
1075
|
### Common Deployment Scenarios
|
|
1026
1076
|
|
|
@@ -1061,12 +1111,21 @@ end
|
|
|
1061
1111
|
|
|
1062
1112
|
```ruby
|
|
1063
1113
|
task :update_config, roles: :web do
|
|
1064
|
-
|
|
1065
|
-
environment: "production",
|
|
1066
|
-
|
|
1067
|
-
|
|
1114
|
+
template "/etc/app/config.yml", source: "./config/app.yml.erb",
|
|
1115
|
+
variables: { environment: "production", database_url: ENV['DATABASE_URL'], redis_url: ENV['REDIS_URL'] }
|
|
1116
|
+
service "app", action: :reload
|
|
1117
|
+
end
|
|
1118
|
+
```
|
|
1068
1119
|
|
|
1069
|
-
|
|
1120
|
+
#### Directory Sync Deployment
|
|
1121
|
+
|
|
1122
|
+
```ruby
|
|
1123
|
+
task :deploy_app, roles: :web do
|
|
1124
|
+
sync "./app", "/var/www/app",
|
|
1125
|
+
ignore: [".git", "*.log", "node_modules", ".env.local", "*.tmp"],
|
|
1126
|
+
delete: true
|
|
1127
|
+
sync "./config", "/etc/app", exclude: ["*.example", "*.bak"]
|
|
1128
|
+
service "app", action: :restart
|
|
1070
1129
|
end
|
|
1071
1130
|
```
|
|
1072
1131
|
|
|
@@ -1079,7 +1138,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
1079
1138
|
- **GitHub**: https://github.com/kevin197011/kdeploy
|
|
1080
1139
|
- **RubyGems**: https://rubygems.org/gems/kdeploy
|
|
1081
1140
|
- **Issues**: https://github.com/kevin197011/kdeploy/issues
|
|
1082
|
-
- **
|
|
1141
|
+
- **Sample**: [sample/](sample/) directory (includes Vagrant config)
|
|
1083
1142
|
|
|
1084
1143
|
## 🙏 Acknowledgments
|
|
1085
1144
|
|
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
|