kdeploy 1.2.41 → 1.3.3

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: d9bab827b041d7b33e3486ddcddc0956fa7b00fddbd20275709c8e23f5201f7a
4
- data.tar.gz: c7eee9c23f27b9541a4ef09c253ae5bffa9e9ae5715a941eb36f363afb5a6122
3
+ metadata.gz: bd3c4b1e7ad1dc2062bfa0e732144b64f0c3c81655320dc96defeaed10816392
4
+ data.tar.gz: 2ac13e3f6b7093042d4a313c139ad997e59e06b1fa2d6dc7372dc33919f3eebc
5
5
  SHA512:
6
- metadata.gz: 9fb85d1b6243a36259f67a6d9c96e1028190a7d650a5b625d4b31f76f5bd2eb12adcce06e07c7c12177ffaa128fa32a561e8b8092674ca51df2d4c6e6d5d2790
7
- data.tar.gz: 6dd1ad95b47ecf2b0d8d09ee3f2994c4caccfb248894f9ca0c374a66fe9121a427726421ad9ceb0ce30512adb34080a7c39bb7c7789022dcff5531ec8131e003
6
+ metadata.gz: f224afb29cf1d419a80aa7797841b1fd935030841c0f80ad403e25573635e45956b8779c6518f6c37b4150c110c19f200baa1247ea03353e442ba6e0a5061a65
7
+ data.tar.gz: d6cae91436717b81f3a527707f7bed7e93107d868bf457b3ec9e62d859b42b241103f8600315155fd5e9409a09671319f487f7e0e4ea0f276d9dd8a48cd3ec2a
data/README.md CHANGED
@@ -207,6 +207,10 @@ kdeploy execute deploy.rb deploy_web
207
207
  - `--format FORMAT`: 输出格式(`text`|`json`,默认 `text`)
208
208
  - `--retries N`: 网络相关操作重试次数(默认 `0`)
209
209
  - `--retry-delay SECONDS`: 每次重试间隔秒数(默认 `1`)
210
+ - `--retry-on-nonzero`: 非零退出码重试开关(默认 `false`)
211
+ - `--timeout SECONDS`: 单 host 执行超时(秒,默认不启用)
212
+ - `--step-timeout SECONDS`: 单 step 执行超时(秒,默认不启用)
213
+ - `--retry-policy JSON`: 重试策略 JSON(覆盖 `.kdeploy.yml`)
210
214
 
211
215
  **示例:**
212
216
  ```bash
@@ -228,6 +232,21 @@ kdeploy execute deploy.rb deploy_web --format json --no-banner
228
232
  # 重试网络抖动导致的失败
229
233
  kdeploy execute deploy.rb deploy_web --retries 3 --retry-delay 1
230
234
 
235
+ # 对非零退出码进行重试
236
+ kdeploy execute deploy.rb deploy_web --retries 2 --retry-on-nonzero
237
+
238
+ # 设置单 host 超时(秒)
239
+ kdeploy execute deploy.rb deploy_web --timeout 120
240
+
241
+ # 设置单 step 超时(秒)
242
+ kdeploy execute deploy.rb deploy_web --step-timeout 30
243
+
244
+ # 使用 CLI 覆盖重试策略(JSON)
245
+ kdeploy execute deploy.rb deploy_web --retry-policy '{"run":{"retries":2,"retry_on_exit_codes":[2]}}'
246
+
247
+ # 使用文件覆盖重试策略(JSON)
248
+ kdeploy execute deploy.rb deploy_web --retry-policy-file ./retry_policy.example.json
249
+
231
250
  # 组合选项
232
251
  kdeploy execute deploy.rb deploy_web --limit web01 --parallel 3 --dry-run
233
252
  ```
@@ -501,6 +520,14 @@ sync "./app", "/var/www/app",
501
520
  # 排除特定文件(与 ignore 相同,但语义更清晰)
502
521
  sync "./config", "/etc/app",
503
522
  exclude: ["*.example", "*.bak", ".env.local"]
523
+
524
+ # 启用快速同步(本地/远端均有 rsync 时优先使用)
525
+ sync "./app", "/var/www/app",
526
+ fast: true
527
+
528
+ # 设置同步并行度
529
+ sync "./app", "/var/www/app",
530
+ parallel: 4
504
531
  ```
505
532
 
506
533
  **参数:**
@@ -509,6 +536,8 @@ sync "./config", "/etc/app",
509
536
  - `ignore`: 要忽略的文件/目录模式数组(支持 .gitignore 风格的通配符)
510
537
  - `exclude`: 与 `ignore` 相同,用于语义清晰
511
538
  - `delete`: 布尔值,是否删除远程目录中不存在于源目录的文件(默认: false)
539
+ - `fast`: 布尔值,启用快速同步路径(优先 rsync,默认: false)
540
+ - `parallel`: 上传并行度(默认: 1)
512
541
 
513
542
  **忽略模式支持:**
514
543
  - `*.log` - 匹配所有 .log 文件
@@ -682,10 +711,24 @@ export KDEPLOY_SSH_TIMEOUT=60
682
711
  parallel: 5
683
712
  ssh_timeout: 60
684
713
  verify_host_key: true
714
+ retries: 2
715
+ retry_delay: 1
716
+ retry_on_nonzero: false
717
+ step_timeout: 30
718
+ sync_fast: false
719
+ sync_parallel: 4
720
+ retry_policy:
721
+ run:
722
+ retries: 2
723
+ retry_on_exit_codes: [2, 255]
724
+ upload:
725
+ retries: 0
685
726
  ```
686
727
 
687
728
  配置文件会自动从当前目录向上查找,直到找到 `.kdeploy.yml` 文件。
688
729
 
730
+ **重试策略示例文件**: `retry_policy.example.json`
731
+
689
732
  ## 🔧 高级用法
690
733
 
691
734
  ### 条件执行
@@ -702,6 +745,16 @@ task :deploy do
702
745
  end
703
746
  ```
704
747
 
748
+ ### 重试策略示例
749
+
750
+ 你可以通过文件覆盖重试策略:
751
+
752
+ ```bash
753
+ kdeploy execute deploy.rb deploy_web --retry-policy-file ./retry_policy.example.json
754
+ ```
755
+
756
+ 示例文件见:`retry_policy.example.json` / `retry_policy.example.yml`
757
+
705
758
  ### 循环主机
706
759
 
707
760
  ```ruby
@@ -1141,4 +1194,3 @@ end
1141
1194
  ---
1142
1195
 
1143
1196
  **为 DevOps 社区用 ❤️ 制作**
1144
-
data/README_EN.md CHANGED
@@ -206,6 +206,11 @@ kdeploy execute deploy.rb deploy_web
206
206
  - `--format FORMAT`: Output format (`text`|`json`, default `text`)
207
207
  - `--retries N`: Retry count for network operations (default `0`)
208
208
  - `--retry-delay SECONDS`: Delay between retries in seconds (default `1`)
209
+ - `--retry-on-nonzero`: Retry commands on nonzero exit status (default `false`)
210
+ - `--timeout SECONDS`: Per-host execution timeout in seconds (default: none)
211
+ - `--step-timeout SECONDS`: Per-step execution timeout in seconds (default: none)
212
+ - `--retry-policy JSON`: Retry policy JSON (overrides `.kdeploy.yml`)
213
+ - `--retry-policy-file PATH`: Retry policy JSON file (overrides `.kdeploy.yml`)
209
214
 
210
215
  **Examples:**
211
216
  ```bash
@@ -227,6 +232,21 @@ kdeploy execute deploy.rb deploy_web --format json --no-banner
227
232
  # Retry transient network failures
228
233
  kdeploy execute deploy.rb deploy_web --retries 3 --retry-delay 1
229
234
 
235
+ # Retry on nonzero exit status
236
+ kdeploy execute deploy.rb deploy_web --retries 2 --retry-on-nonzero
237
+
238
+ # Set per-host timeout (seconds)
239
+ kdeploy execute deploy.rb deploy_web --timeout 120
240
+
241
+ # Set per-step timeout (seconds)
242
+ kdeploy execute deploy.rb deploy_web --step-timeout 30
243
+
244
+ # Override retry policy via CLI JSON
245
+ kdeploy execute deploy.rb deploy_web --retry-policy '{"run":{"retries":2,"retry_on_exit_codes":[2]}}'
246
+
247
+ # Override retry policy via file
248
+ kdeploy execute deploy.rb deploy_web --retry-policy-file ./retry_policy.example.json
249
+
230
250
  # Combine options
231
251
  kdeploy execute deploy.rb deploy_web --limit web01 --parallel 3 --dry-run
232
252
  ```
@@ -480,6 +500,45 @@ upload_template "./config/nginx.conf.erb", "/etc/nginx/nginx.conf",
480
500
  - `destination`: Remote file path
481
501
  - `variables`: Hash of variables for template rendering
482
502
 
503
+ #### `sync` - Directory Sync
504
+
505
+ Recursively sync a local directory to a remote server with filtering options.
506
+
507
+ ```ruby
508
+ # Basic sync
509
+ sync "./app", "/var/www/app"
510
+
511
+ # Ignore specific files/dirs
512
+ sync "./app", "/var/www/app",
513
+ ignore: [".git", "*.log", "node_modules", "*.tmp"]
514
+
515
+ # Delete remote files not present locally
516
+ sync "./app", "/var/www/app",
517
+ ignore: [".git", "*.log"],
518
+ delete: true
519
+
520
+ # Exclude files (alias of ignore)
521
+ sync "./config", "/etc/app",
522
+ exclude: ["*.example", "*.bak", ".env.local"]
523
+
524
+ # Fast sync (prefer rsync when available)
525
+ sync "./app", "/var/www/app",
526
+ fast: true
527
+
528
+ # Parallel sync uploads
529
+ sync "./app", "/var/www/app",
530
+ parallel: 4
531
+ ```
532
+
533
+ **Parameters:**
534
+ - `source`: Local source directory path
535
+ - `destination`: Remote destination directory path
536
+ - `ignore`: Patterns to ignore (gitignore-style)
537
+ - `exclude`: Same as `ignore` for clarity
538
+ - `delete`: Delete remote files not present locally (default: false)
539
+ - `fast`: Enable fast sync path (prefer rsync when available, default: false)
540
+ - `parallel`: Upload concurrency for sync (default: 1)
541
+
483
542
  ### Chef-Style Resource DSL
484
543
 
485
544
  Kdeploy provides a declarative resource DSL similar to Chef, which can replace or mix with low-level primitives (`run`, `upload`, `upload_template`).
@@ -639,7 +698,21 @@ For project-specific configuration, create a `.kdeploy.yml`:
639
698
  parallel: 5
640
699
  ssh_timeout: 60
641
700
  verify_host_key: true
642
- ```
701
+ retries: 2
702
+ retry_delay: 1
703
+ retry_on_nonzero: false
704
+ step_timeout: 30
705
+ sync_fast: false
706
+ sync_parallel: 4
707
+ retry_policy:
708
+ run:
709
+ retries: 2
710
+ retry_on_exit_codes: [2, 255]
711
+ upload:
712
+ retries: 0
713
+ ```
714
+
715
+ **Retry policy example file**: `retry_policy.example.json`
643
716
 
644
717
  ## 🔧 Advanced Usage
645
718
 
@@ -657,6 +730,16 @@ task :deploy do
657
730
  end
658
731
  ```
659
732
 
733
+ ### Retry Policy Example
734
+
735
+ You can override retry policy via file:
736
+
737
+ ```bash
738
+ kdeploy execute deploy.rb deploy_web --retry-policy-file ./retry_policy.example.json
739
+ ```
740
+
741
+ Example files: `retry_policy.example.json` / `retry_policy.example.yml`
742
+
660
743
  ### Looping Over Hosts
661
744
 
662
745
  ```ruby
@@ -1123,7 +1206,8 @@ end
1123
1206
  task :deploy_app, roles: :web do
1124
1207
  sync "./app", "/var/www/app",
1125
1208
  ignore: [".git", "*.log", "node_modules", ".env.local", "*.tmp"],
1126
- delete: true
1209
+ delete: true,
1210
+ fast: true # prefer rsync when available
1127
1211
  sync "./config", "/etc/app", exclude: ["*.example", "*.bak"]
1128
1212
  service "app", action: :restart
1129
1213
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'thor'
4
4
  require 'json'
5
+ require 'yaml'
5
6
  require 'pastel'
6
7
  require 'tty-table'
7
8
  require 'tty-box'
@@ -51,6 +52,11 @@ module Kdeploy
51
52
  method_option :format, type: :string, default: 'text', desc: 'Output format (text|json)'
52
53
  method_option :retries, type: :numeric, desc: 'Retry count for network operations (default: 0)'
53
54
  method_option :retry_delay, type: :numeric, desc: 'Retry delay seconds (default: 1)'
55
+ method_option :retry_on_nonzero, type: :boolean, desc: 'Retry commands on nonzero exit status (default: false)'
56
+ method_option :timeout, type: :numeric, desc: 'Per-host execution timeout seconds (default: none)'
57
+ method_option :step_timeout, type: :numeric, desc: 'Per-step execution timeout seconds (default: none)'
58
+ method_option :retry_policy, type: :string, desc: 'Retry policy JSON to override config file'
59
+ method_option :retry_policy_file, type: :string, desc: 'Retry policy JSON file to override config file'
54
60
  def execute(task_file, task_name = nil)
55
61
  load_config_file
56
62
  show_banner_once
@@ -75,7 +81,7 @@ module Kdeploy
75
81
 
76
82
  def load_task_file(file)
77
83
  validate_task_file(file)
78
- # instance_eval 并传递顶层 binding,兼容 heredoc
84
+ # Use module_eval with top-level binding to keep heredoc compatible
79
85
  self.class.module_eval(File.read(file), file)
80
86
  rescue StandardError => e
81
87
  raise FileNotFoundError, file if e.message.include?('not found')
@@ -267,6 +273,11 @@ module Kdeploy
267
273
  debug_mode = options[:debug] || false
268
274
  retries = options[:retries].nil? ? Configuration.default_retries : options[:retries]
269
275
  retry_delay = options[:retry_delay].nil? ? Configuration.default_retry_delay : options[:retry_delay]
276
+ retry_on_nonzero =
277
+ options[:retry_on_nonzero].nil? ? Configuration.default_retry_on_nonzero : options[:retry_on_nonzero]
278
+ host_timeout = options[:timeout].nil? ? Configuration.default_host_timeout : options[:timeout]
279
+ step_timeout = options[:step_timeout].nil? ? Configuration.default_step_timeout : options[:step_timeout]
280
+ retry_policy = resolve_retry_policy
270
281
  base_dir = @task_file_dir
271
282
  runner = Runner.new(
272
283
  hosts, self.class.kdeploy_tasks,
@@ -275,7 +286,11 @@ module Kdeploy
275
286
  debug: debug_mode,
276
287
  base_dir: base_dir,
277
288
  retries: retries,
278
- retry_delay: retry_delay
289
+ retry_delay: retry_delay,
290
+ retry_on_nonzero: retry_on_nonzero,
291
+ host_timeout: host_timeout,
292
+ step_timeout: step_timeout,
293
+ retry_policy: retry_policy
279
294
  )
280
295
  results = runner.run(task)
281
296
  if options[:format] == 'json'
@@ -363,13 +378,13 @@ module Kdeploy
363
378
  duration: step[:duration]
364
379
  }
365
380
 
366
- out[:result] = step[:result] if step[:type] == :sync
367
-
368
- if options[:debug] && step[:type] == :run && step[:output].is_a?(Hash)
381
+ if step[:output].is_a?(Hash)
369
382
  out[:stdout] = step[:output][:stdout]
370
383
  out[:stderr] = step[:output][:stderr]
384
+ out[:exit_status] = step[:output][:exit_status]
371
385
  end
372
386
 
387
+ out[:result] = step[:result] if step[:type] == :sync
373
388
  out
374
389
  end
375
390
 
@@ -388,7 +403,9 @@ module Kdeploy
388
403
  destination: cmd[:destination],
389
404
  ignore: cmd[:ignore] || [],
390
405
  exclude: cmd[:exclude] || [],
391
- delete: cmd[:delete] || false
406
+ delete: cmd[:delete] || false,
407
+ fast: cmd[:fast],
408
+ parallel: cmd[:parallel]
392
409
  }
393
410
  else
394
411
  { type: cmd[:type].to_s }
@@ -400,11 +417,61 @@ module Kdeploy
400
417
 
401
418
  if result[:output].is_a?(Array)
402
419
  result[:output].map do |o|
403
- o[:output][:stderr] if o[:output].is_a?(Hash)
420
+ next unless o.is_a?(Hash)
421
+
422
+ err = o[:output]
423
+ next unless err.is_a?(Hash)
424
+
425
+ pieces = []
426
+ pieces << "command=#{err[:command]}" if err[:command]
427
+ pieces << "exit_status=#{err[:exit_status]}" if err[:exit_status]
428
+ pieces << "stderr=#{err[:stderr]}" if err[:stderr]
429
+ pieces.join(' ')
404
430
  end.compact.join("\n")
405
431
  else
406
432
  result[:output].to_s
407
433
  end
408
434
  end
435
+
436
+ def parse_retry_policy(raw)
437
+ policy = JSON.parse(raw)
438
+ raise ArgumentError, 'retry_policy must be a JSON object' unless policy.is_a?(Hash)
439
+
440
+ policy
441
+ rescue JSON::ParserError => e
442
+ raise ArgumentError, "retry_policy JSON parse error: #{e.message}"
443
+ end
444
+
445
+ def resolve_retry_policy
446
+ if options[:retry_policy_file]
447
+ path = options[:retry_policy_file]
448
+ unless File.exist?(path)
449
+ raise ArgumentError,
450
+ "retry_policy file not found: #{path} (examples: retry_policy.example.json / retry_policy.example.yml)"
451
+ end
452
+
453
+ return parse_retry_policy_file(path)
454
+ end
455
+ return parse_retry_policy(options[:retry_policy]) if options[:retry_policy]
456
+
457
+ Configuration.default_retry_policy
458
+ end
459
+
460
+ def parse_retry_policy_file(path)
461
+ ext = File.extname(path).downcase
462
+ raw = File.read(path)
463
+ policy =
464
+ case ext
465
+ when '.yml', '.yaml'
466
+ YAML.safe_load(raw) || {}
467
+ else
468
+ JSON.parse(raw)
469
+ end
470
+ raise ArgumentError, 'retry_policy must be a JSON/YAML object' unless policy.is_a?(Hash)
471
+
472
+ policy
473
+ rescue JSON::ParserError, Psych::SyntaxError => e
474
+ raise ArgumentError, "retry_policy parse error: #{e.message}"
475
+ end
409
476
  end
410
477
  end
@@ -34,6 +34,11 @@ module Kdeploy
34
34
  #{@pastel.dim(' --format FORMAT')} Output format (text|json)
35
35
  #{@pastel.dim(' --retries N')} Retry count for network operations (default: 0; overridden by .kdeploy.yml)
36
36
  #{@pastel.dim(' --retry-delay SECONDS')} Retry delay seconds (default: 1; overridden by .kdeploy.yml)
37
+ #{@pastel.dim(' --retry-on-nonzero')} Retry commands on nonzero exit status (default: false; overridden by .kdeploy.yml)
38
+ #{@pastel.dim(' --timeout SECONDS')} Per-host execution timeout seconds (default: none; overridden by .kdeploy.yml)
39
+ #{@pastel.dim(' --step-timeout SECONDS')} Per-step execution timeout seconds (default: none; overridden by .kdeploy.yml)
40
+ #{@pastel.dim(' --retry-policy JSON')} Retry policy JSON (overrides .kdeploy.yml)
41
+ #{@pastel.dim(' --retry-policy-file PATH')} Retry policy JSON file (overrides .kdeploy.yml)
37
42
 
38
43
  #{@pastel.bright_yellow('🆕')} #{@pastel.bright_white('init [DIR]')} Initialize new deployment project
39
44
  #{@pastel.bright_yellow('ℹ️')} #{@pastel.bright_white('version')} Show version information
@@ -10,6 +10,12 @@ module Kdeploy
10
10
  DEFAULT_VERIFY_HOST_KEY = :never
11
11
  DEFAULT_RETRIES = 0
12
12
  DEFAULT_RETRY_DELAY = 1
13
+ DEFAULT_HOST_TIMEOUT = nil
14
+ DEFAULT_RETRY_ON_NONZERO = false
15
+ DEFAULT_SYNC_FAST = false
16
+ DEFAULT_STEP_TIMEOUT = nil
17
+ DEFAULT_RETRY_POLICY = nil
18
+ DEFAULT_SYNC_PARALLEL = 1
13
19
  CONFIG_FILE_NAME = '.kdeploy.yml'
14
20
 
15
21
  class << self
@@ -17,7 +23,13 @@ module Kdeploy
17
23
  :default_ssh_timeout,
18
24
  :default_verify_host_key,
19
25
  :default_retries,
20
- :default_retry_delay
26
+ :default_retry_delay,
27
+ :default_host_timeout,
28
+ :default_retry_on_nonzero,
29
+ :default_sync_fast,
30
+ :default_step_timeout,
31
+ :default_retry_policy,
32
+ :default_sync_parallel
21
33
 
22
34
  def reset
23
35
  @default_parallel = DEFAULT_PARALLEL
@@ -25,6 +37,12 @@ module Kdeploy
25
37
  @default_verify_host_key = DEFAULT_VERIFY_HOST_KEY
26
38
  @default_retries = DEFAULT_RETRIES
27
39
  @default_retry_delay = DEFAULT_RETRY_DELAY
40
+ @default_host_timeout = DEFAULT_HOST_TIMEOUT
41
+ @default_retry_on_nonzero = DEFAULT_RETRY_ON_NONZERO
42
+ @default_sync_fast = DEFAULT_SYNC_FAST
43
+ @default_step_timeout = DEFAULT_STEP_TIMEOUT
44
+ @default_retry_policy = DEFAULT_RETRY_POLICY
45
+ @default_sync_parallel = DEFAULT_SYNC_PARALLEL
28
46
  end
29
47
 
30
48
  def load_from_file(config_path = nil)
@@ -64,6 +82,12 @@ module Kdeploy
64
82
  @default_verify_host_key = parse_verify_host_key(config['verify_host_key']) if config.key?('verify_host_key')
65
83
  @default_retries = config['retries'] if config.key?('retries')
66
84
  @default_retry_delay = config['retry_delay'] if config.key?('retry_delay')
85
+ @default_host_timeout = config['host_timeout'] if config.key?('host_timeout')
86
+ @default_retry_on_nonzero = config['retry_on_nonzero'] if config.key?('retry_on_nonzero')
87
+ @default_sync_fast = config['sync_fast'] if config.key?('sync_fast')
88
+ @default_step_timeout = config['step_timeout'] if config.key?('step_timeout')
89
+ @default_retry_policy = config['retry_policy'] if config.key?('retry_policy')
90
+ @default_sync_parallel = config['sync_parallel'] if config.key?('sync_parallel')
67
91
  end
68
92
 
69
93
  def parse_verify_host_key(value)
@@ -161,7 +161,7 @@ module Kdeploy
161
161
  }
162
162
  end
163
163
 
164
- def sync(source, destination, ignore: [], delete: false, exclude: [])
164
+ def sync(source, destination, ignore: [], delete: false, exclude: [], fast: nil, parallel: nil)
165
165
  @kdeploy_commands ||= []
166
166
  @kdeploy_commands << {
167
167
  type: :sync,
@@ -169,7 +169,9 @@ module Kdeploy
169
169
  destination: destination,
170
170
  ignore: Array(ignore),
171
171
  exclude: Array(exclude),
172
- delete: delete
172
+ delete: delete,
173
+ fast: fast,
174
+ parallel: parallel
173
175
  }
174
176
  end
175
177
 
@@ -177,14 +179,14 @@ module Kdeploy
177
179
  # Chef-style resource DSL (compiles to run/upload/upload_template)
178
180
  # -------------------------------------------------------------------------
179
181
 
180
- # 安装系统包。默认 apt 平台;支持 platform: :yum
182
+ # Install a system package. Defaults to apt; supports platform: :yum.
181
183
  def package(name, version: nil, platform: :apt)
182
184
  @kdeploy_commands ||= []
183
185
  cmd = build_package_command(name, version, platform)
184
186
  @kdeploy_commands << { type: :run, command: cmd, sudo: true }
185
187
  end
186
188
 
187
- # 管理系统服务(systemd)。action 支持 :start, :stop, :restart, :reload, :enable, :disable
189
+ # Manage a systemd service. action supports :start, :stop, :restart, :reload, :enable, :disable.
188
190
  def service(name, action: :start)
189
191
  @kdeploy_commands ||= []
190
192
  actions = Array(action)
@@ -194,7 +196,7 @@ module Kdeploy
194
196
  end
195
197
  end
196
198
 
197
- # 部署 ERB 模板到远程路径。支持 block 或关键字参数。
199
+ # Deploy an ERB template to a remote path. Supports block and keyword args.
198
200
  def template(destination, source: nil, variables: nil, &block)
199
201
  @kdeploy_commands ||= []
200
202
  if block
@@ -211,13 +213,13 @@ module Kdeploy
211
213
  upload_template(src, destination, vars)
212
214
  end
213
215
 
214
- # 上传本地文件到远程路径。
216
+ # Upload a local file to a remote path.
215
217
  def file(destination, source:)
216
218
  @kdeploy_commands ||= []
217
219
  upload(source, destination)
218
220
  end
219
221
 
220
- # 确保远程目录存在。支持 mode 参数。
222
+ # Ensure a remote directory exists. Supports mode option.
221
223
  def directory(path, mode: nil)
222
224
  @kdeploy_commands ||= []
223
225
  cmd = "mkdir -p #{Shellwords.escape(path.to_s)}"
@@ -96,11 +96,11 @@ module Kdeploy
96
96
  end
97
97
 
98
98
  def create_config_files
99
- # 创建配置目录
99
+ # Create config directory
100
100
  config_dir = File.join(@target_dir, 'config')
101
101
  FileUtils.mkdir_p(config_dir)
102
102
 
103
- # 创建 Nginx ERB 模板
103
+ # Create Nginx ERB template
104
104
  File.write(File.join(config_dir, 'nginx.conf.erb'), <<~CONF)
105
105
  user nginx;
106
106
  worker_processes <%= worker_processes %>;
@@ -152,7 +152,7 @@ module Kdeploy
152
152
  }
153
153
  CONF
154
154
 
155
- # 创建静态配置文件示例
155
+ # Create static config file example
156
156
  File.write(File.join(config_dir, 'app.conf'), <<~CONF)
157
157
  location /api {
158
158
  proxy_pass http://localhost:3000;
@@ -52,6 +52,13 @@ module Kdeploy
52
52
  attr_reader :original_error
53
53
  end
54
54
 
55
+ # Raised when a step exceeds configured timeout
56
+ class StepTimeoutError < Error
57
+ def initialize(message)
58
+ super("Step timeout: #{message}")
59
+ end
60
+ end
61
+
55
62
  # Raised when configuration is invalid
56
63
  class ConfigurationError < Error
57
64
  def initialize(message)