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 +4 -4
- data/AGENTS.md +18 -0
- data/README.md +78 -6
- data/README_EN.md +78 -6
- data/exe/kdeploy +0 -14
- data/lib/kdeploy/cli.rb +137 -14
- data/lib/kdeploy/command_executor.rb +30 -81
- data/lib/kdeploy/configuration.rb +9 -1
- data/lib/kdeploy/dsl.rb +109 -0
- data/lib/kdeploy/errors.rb +6 -2
- data/lib/kdeploy/executor.rb +23 -3
- data/lib/kdeploy/file_filter.rb +14 -3
- data/lib/kdeploy/help_formatter.rb +12 -1
- data/lib/kdeploy/initializer.rb +22 -30
- data/lib/kdeploy/output_formatter.rb +49 -43
- data/lib/kdeploy/runner.rb +74 -46
- data/lib/kdeploy/version.rb +1 -1
- data/lib/kdeploy.rb +0 -1
- data/r.md +1 -0
- metadata +4 -3
- data/lib/kdeploy/command_grouper.rb +0 -38
|
@@ -3,36 +3,28 @@
|
|
|
3
3
|
module Kdeploy
|
|
4
4
|
# Executes a single command and records execution time
|
|
5
5
|
class CommandExecutor
|
|
6
|
-
def initialize(executor, output, debug: false)
|
|
6
|
+
def initialize(executor, output, debug: false, retries: 0, retry_delay: 1)
|
|
7
7
|
@executor = executor
|
|
8
8
|
@output = output
|
|
9
9
|
@debug = debug
|
|
10
|
+
@retries = retries.to_i
|
|
11
|
+
@retry_delay = retry_delay.to_f
|
|
10
12
|
end
|
|
11
13
|
|
|
12
|
-
def execute_run(command,
|
|
14
|
+
def execute_run(command, _host_name)
|
|
13
15
|
cmd = command[:command]
|
|
14
16
|
use_sudo = command[:sudo]
|
|
15
|
-
show_command_header(host_name, :run, cmd)
|
|
16
|
-
|
|
17
|
-
# Show progress indicator for long-running commands
|
|
18
|
-
pastel = @output.respond_to?(:pastel) ? @output.pastel : Pastel.new
|
|
19
17
|
|
|
20
18
|
result, duration = measure_time do
|
|
21
|
-
@executor.execute(cmd, use_sudo: use_sudo)
|
|
19
|
+
with_retries { @executor.execute(cmd, use_sudo: use_sudo) }
|
|
22
20
|
end
|
|
23
21
|
|
|
24
|
-
# Show execution time if command took more than 1 second
|
|
25
|
-
@output.write_line(pastel.dim(" [completed in #{format('%.2f', duration)}s]")) if duration > 1.0
|
|
26
|
-
|
|
27
|
-
# Show command output only in debug mode
|
|
28
|
-
show_command_output(result) if @debug
|
|
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
|
-
@executor.upload(command[:source], command[:destination])
|
|
27
|
+
with_retries { @executor.upload(command[:source], command[:destination]) }
|
|
36
28
|
end
|
|
37
29
|
{
|
|
38
30
|
command: "upload: #{command[:source]} -> #{command[:destination]}",
|
|
@@ -41,10 +33,11 @@ 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
|
|
39
|
+
@executor.upload_template(command[:source], command[:destination], command[:variables])
|
|
40
|
+
end
|
|
48
41
|
end
|
|
49
42
|
{
|
|
50
43
|
command: "upload_template: #{command[:source]} -> #{command[:destination]}",
|
|
@@ -53,20 +46,20 @@ module Kdeploy
|
|
|
53
46
|
}
|
|
54
47
|
end
|
|
55
48
|
|
|
56
|
-
def execute_sync(command,
|
|
49
|
+
def execute_sync(command, _host_name)
|
|
57
50
|
source = command[:source]
|
|
58
51
|
destination = command[:destination]
|
|
59
|
-
description = build_sync_description(source, destination, command[:delete])
|
|
60
|
-
show_command_header(host_name, :sync, description)
|
|
61
52
|
|
|
62
53
|
result, duration = measure_time do
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
54
|
+
with_retries do
|
|
55
|
+
@executor.sync_directory(
|
|
56
|
+
source,
|
|
57
|
+
destination,
|
|
58
|
+
ignore: command[:ignore] || [],
|
|
59
|
+
exclude: command[:exclude] || [],
|
|
60
|
+
delete: command[:delete] || false
|
|
61
|
+
)
|
|
62
|
+
end
|
|
70
63
|
end
|
|
71
64
|
|
|
72
65
|
build_sync_result(source, destination, result, duration)
|
|
@@ -74,12 +67,6 @@ module Kdeploy
|
|
|
74
67
|
|
|
75
68
|
private
|
|
76
69
|
|
|
77
|
-
def build_sync_description(source, destination, delete)
|
|
78
|
-
desc = "sync: #{source} -> #{destination}"
|
|
79
|
-
desc += " (delete: #{delete})" if delete
|
|
80
|
-
desc
|
|
81
|
-
end
|
|
82
|
-
|
|
83
70
|
def build_sync_result(source, destination, result, duration)
|
|
84
71
|
{
|
|
85
72
|
command: "sync: #{source} -> #{destination}",
|
|
@@ -99,54 +86,16 @@ module Kdeploy
|
|
|
99
86
|
[result, duration]
|
|
100
87
|
end
|
|
101
88
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def show_stdout(stdout)
|
|
111
|
-
return unless stdout && !stdout.empty?
|
|
112
|
-
|
|
113
|
-
stdout.each_line do |line|
|
|
114
|
-
@output.write_line(" #{line.rstrip}") unless line.strip.empty?
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def show_stderr(stderr, pastel)
|
|
119
|
-
return unless stderr && !stderr.empty?
|
|
120
|
-
|
|
121
|
-
stderr.each_line do |line|
|
|
122
|
-
@output.write_line(pastel.green(" #{line.rstrip}")) unless line.strip.empty?
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def show_command_header(host_name, type, description)
|
|
127
|
-
# Don't show command header during execution - it will be shown in results
|
|
128
|
-
# This reduces noise during execution
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def pastel_instance
|
|
132
|
-
@output.respond_to?(:pastel) ? @output.pastel : Pastel.new
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def format_command_by_type(type, description, pastel)
|
|
136
|
-
case type
|
|
137
|
-
when :run
|
|
138
|
-
format_run_command(description, pastel)
|
|
139
|
-
when :upload
|
|
140
|
-
@output.write_line(pastel.green(" [upload] #{description}"))
|
|
141
|
-
when :upload_template
|
|
142
|
-
@output.write_line(pastel.yellow(" [template] #{description}"))
|
|
143
|
-
end
|
|
144
|
-
end
|
|
89
|
+
def with_retries
|
|
90
|
+
attempts = 0
|
|
91
|
+
begin
|
|
92
|
+
attempts += 1
|
|
93
|
+
yield
|
|
94
|
+
rescue SSHError, SCPError, TemplateError
|
|
95
|
+
raise if attempts > (@retries + 1)
|
|
145
96
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
description.lines[1..].each do |line|
|
|
149
|
-
@output.write_line(" > #{line.strip}") unless line.strip.empty?
|
|
97
|
+
sleep(@retry_delay) if @retry_delay.positive?
|
|
98
|
+
retry
|
|
150
99
|
end
|
|
151
100
|
end
|
|
152
101
|
end
|
|
@@ -8,17 +8,23 @@ module Kdeploy
|
|
|
8
8
|
DEFAULT_PARALLEL = 10
|
|
9
9
|
DEFAULT_SSH_TIMEOUT = 30
|
|
10
10
|
DEFAULT_VERIFY_HOST_KEY = :never
|
|
11
|
+
DEFAULT_RETRIES = 0
|
|
12
|
+
DEFAULT_RETRY_DELAY = 1
|
|
11
13
|
CONFIG_FILE_NAME = '.kdeploy.yml'
|
|
12
14
|
|
|
13
15
|
class << self
|
|
14
16
|
attr_accessor :default_parallel,
|
|
15
17
|
:default_ssh_timeout,
|
|
16
|
-
:default_verify_host_key
|
|
18
|
+
:default_verify_host_key,
|
|
19
|
+
:default_retries,
|
|
20
|
+
:default_retry_delay
|
|
17
21
|
|
|
18
22
|
def reset
|
|
19
23
|
@default_parallel = DEFAULT_PARALLEL
|
|
20
24
|
@default_ssh_timeout = DEFAULT_SSH_TIMEOUT
|
|
21
25
|
@default_verify_host_key = DEFAULT_VERIFY_HOST_KEY
|
|
26
|
+
@default_retries = DEFAULT_RETRIES
|
|
27
|
+
@default_retry_delay = DEFAULT_RETRY_DELAY
|
|
22
28
|
end
|
|
23
29
|
|
|
24
30
|
def load_from_file(config_path = nil)
|
|
@@ -56,6 +62,8 @@ module Kdeploy
|
|
|
56
62
|
@default_parallel = config['parallel'] if config.key?('parallel')
|
|
57
63
|
@default_ssh_timeout = config['ssh_timeout'] if config.key?('ssh_timeout')
|
|
58
64
|
@default_verify_host_key = parse_verify_host_key(config['verify_host_key']) if config.key?('verify_host_key')
|
|
65
|
+
@default_retries = config['retries'] if config.key?('retries')
|
|
66
|
+
@default_retry_delay = config['retry_delay'] if config.key?('retry_delay')
|
|
59
67
|
end
|
|
60
68
|
|
|
61
69
|
def parse_verify_host_key(value)
|
data/lib/kdeploy/dsl.rb
CHANGED
|
@@ -1,16 +1,56 @@
|
|
|
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
|
|
27
|
+
def self.included(base)
|
|
28
|
+
# Support `include Kdeploy::DSL` by promoting the DSL methods to class methods.
|
|
29
|
+
# This keeps tests and external integrations simpler while preserving the
|
|
30
|
+
# primary usage pattern (CLI uses `extend DSL`).
|
|
31
|
+
base.extend(self)
|
|
32
|
+
end
|
|
33
|
+
|
|
8
34
|
def self.extended(base)
|
|
9
35
|
base.instance_variable_set(:@kdeploy_hosts, {})
|
|
10
36
|
base.instance_variable_set(:@kdeploy_tasks, {})
|
|
11
37
|
base.instance_variable_set(:@kdeploy_roles, {})
|
|
12
38
|
end
|
|
13
39
|
|
|
40
|
+
# Stable read accessors for tests/integrations.
|
|
41
|
+
# Keep these as aliases so internal storage can evolve without breaking callers.
|
|
42
|
+
def hosts
|
|
43
|
+
kdeploy_hosts
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def tasks
|
|
47
|
+
kdeploy_tasks
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def roles
|
|
51
|
+
kdeploy_roles
|
|
52
|
+
end
|
|
53
|
+
|
|
14
54
|
def kdeploy_hosts
|
|
15
55
|
@kdeploy_hosts ||= {}
|
|
16
56
|
end
|
|
@@ -133,6 +173,60 @@ module Kdeploy
|
|
|
133
173
|
}
|
|
134
174
|
end
|
|
135
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
|
+
|
|
136
230
|
def inventory(&block)
|
|
137
231
|
instance_eval(&block) if block_given?
|
|
138
232
|
end
|
|
@@ -167,5 +261,20 @@ module Kdeploy
|
|
|
167
261
|
end
|
|
168
262
|
end
|
|
169
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
|
|
170
279
|
end
|
|
171
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/file_filter.rb
CHANGED
|
@@ -47,8 +47,14 @@ module Kdeploy
|
|
|
47
47
|
.gsub('[!', '[^') # [^...] negation
|
|
48
48
|
.gsub('[', '[') # Character class
|
|
49
49
|
|
|
50
|
-
#
|
|
51
|
-
|
|
50
|
+
# gitignore semantics: patterns without a slash match in any directory.
|
|
51
|
+
# e.g. "*.log" should match "a.log" and "logs/a.log".
|
|
52
|
+
if !pattern.include?('/') && !pattern.start_with?('**')
|
|
53
|
+
regex_str = "(?:^|.*/)#{regex_str}"
|
|
54
|
+
elsif !pattern.start_with?('**')
|
|
55
|
+
regex_str = "^#{regex_str}"
|
|
56
|
+
end
|
|
57
|
+
|
|
52
58
|
# Match end of string or directory separator
|
|
53
59
|
regex_str = "#{regex_str}(/|$)" unless pattern.end_with?('*') || pattern.end_with?('**')
|
|
54
60
|
|
|
@@ -64,8 +70,13 @@ module Kdeploy
|
|
|
64
70
|
def relative_path_for(file_path, base_path)
|
|
65
71
|
return file_path.to_s unless base_path
|
|
66
72
|
|
|
73
|
+
file = Pathname.new(file_path.to_s)
|
|
74
|
+
|
|
75
|
+
# If caller already passed a relative path (common in sync traversal),
|
|
76
|
+
# keep it as-is to avoid Pathname#relative_path_from prefix errors.
|
|
77
|
+
return file_path.to_s unless file.absolute?
|
|
78
|
+
|
|
67
79
|
base = Pathname.new(base_path)
|
|
68
|
-
file = Pathname.new(file_path)
|
|
69
80
|
file.relative_path_from(base).to_s
|
|
70
81
|
end
|
|
71
82
|
end
|
|
@@ -27,8 +27,13 @@ module Kdeploy
|
|
|
27
27
|
<<~COMMANDS
|
|
28
28
|
#{@pastel.bright_yellow('đ')} #{@pastel.bright_white('execute TASK_FILE [TASK]')} Execute deployment tasks from file
|
|
29
29
|
#{@pastel.dim(' --limit HOSTS')} Limit to specific hosts (comma-separated)
|
|
30
|
-
#{@pastel.dim(' --parallel NUM')} Number of parallel executions (default:
|
|
30
|
+
#{@pastel.dim(' --parallel NUM')} Number of parallel executions (default: 10; overridden by .kdeploy.yml)
|
|
31
31
|
#{@pastel.dim(' --dry-run')} Show what would be done without executing
|
|
32
|
+
#{@pastel.dim(' --debug')} Show detailed command output (stdout/stderr)
|
|
33
|
+
#{@pastel.dim(' --no-banner')} Do not print banner (automation-friendly)
|
|
34
|
+
#{@pastel.dim(' --format FORMAT')} Output format (text|json)
|
|
35
|
+
#{@pastel.dim(' --retries N')} Retry count for network operations (default: 0; overridden by .kdeploy.yml)
|
|
36
|
+
#{@pastel.dim(' --retry-delay SECONDS')} Retry delay seconds (default: 1; overridden by .kdeploy.yml)
|
|
32
37
|
|
|
33
38
|
#{@pastel.bright_yellow('đ')} #{@pastel.bright_white('init [DIR]')} Initialize new deployment project
|
|
34
39
|
#{@pastel.bright_yellow('âšī¸')} #{@pastel.bright_white('version')} Show version information
|
|
@@ -54,6 +59,12 @@ module Kdeploy
|
|
|
54
59
|
|
|
55
60
|
#{@pastel.dim('# Preview deployment')}
|
|
56
61
|
#{@pastel.bright_cyan('kdeploy execute deploy.rb deploy_web --dry-run')}
|
|
62
|
+
|
|
63
|
+
#{@pastel.dim('# Machine-readable output')}
|
|
64
|
+
#{@pastel.bright_cyan('kdeploy execute deploy.rb deploy_web --format json --no-banner')}
|
|
65
|
+
|
|
66
|
+
#{@pastel.dim('# Retry transient network failures')}
|
|
67
|
+
#{@pastel.bright_cyan('kdeploy execute deploy.rb deploy_web --retries 3 --retry-delay 1')}
|
|
57
68
|
EXAMPLES
|
|
58
69
|
end
|
|
59
70
|
|
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
|