kdeploy 1.3.4 → 1.4.1
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/CHANGELOG.md +80 -0
- data/CONTRIBUTING.md +35 -0
- data/LICENSE +21 -0
- data/README.md +17 -7
- data/README_EN.md +18 -7
- data/SECURITY.md +37 -0
- data/lib/kdeploy/cli/cli.rb +64 -9
- data/lib/kdeploy/cli/help_formatter.rb +6 -0
- data/lib/kdeploy/config/configuration.rb +7 -2
- data/lib/kdeploy/dsl/dsl.rb +211 -110
- data/lib/kdeploy/dsl/initializer.rb +20 -0
- data/lib/kdeploy/executor/command_executor.rb +77 -1
- data/lib/kdeploy/executor/executor.rb +91 -17
- data/lib/kdeploy/output/output_formatter.rb +12 -3
- data/lib/kdeploy/runner/runner.rb +74 -22
- data/lib/kdeploy/template/template.rb +2 -1
- data/lib/kdeploy/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f48ad2c400882c0c590c830ae5f2a0e2a29d1a5bd2c978100fcd55f7f2df609
|
|
4
|
+
data.tar.gz: 3490c322e7f539ba8bd2daccd20c3d5176bdfe9e4e03ee051c696a041d57eadc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd89c966452448bc952599312847c7996abd6956e0d6575c6b305b8860bcfaefcf525191819662498b317af58a4378746a882cea0e19a127ae52902a77a8fcf9
|
|
7
|
+
data.tar.gz: bd1f54080f1ddbdbb27be6ff053e744052d5a5bbda309a02879bb04f88fd51af74179170d7a27e9776fe5e66c55929cdf1ee0579a95f36c724cd3b95bd703f0a
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.4.1] - 2026-11-12
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `--report PATH` JSON execution report (`version`, `generated_at`, per-task hosts/steps)
|
|
15
|
+
- `--tags` task filter; `task ..., tags:`
|
|
16
|
+
- `vars` / `--extra-vars` with `%{name}` interpolation in steps
|
|
17
|
+
- `wait_port` / `wait_http` readiness steps (local probes)
|
|
18
|
+
- `on_failure { ... }` cleanup steps after a failed host step
|
|
19
|
+
- CI: Ruby 3.1/3.2/3.3 matrix; lab workflow on `docker/lab` / `lib` PRs
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Default `verify_host_key` is `accept_new` (was `never`)
|
|
24
|
+
- Task plans are `{ commands:, on_failure: }` via per-invocation `TaskContext` (parallel-safe)
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Host timeout clock / abort path; sudo multi-line packing; inventory warnings (carry-over from 1.3.5+)
|
|
29
|
+
|
|
30
|
+
## [1.3.5] - 2026-11-12
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Open-source packaging: `LICENSE`, `CHANGELOG.md`, `SECURITY.md`
|
|
35
|
+
- CI: RSpec + RuboCop before RubyGems publish; weekly Docker lab workflow
|
|
36
|
+
- Safer timeout handling (`Executor#abort!`), sudo password hardening, inventory warnings
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- Rakefile default task is `spec` (no longer auto bump/push)
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- Missing SSH exit status treated as failure
|
|
45
|
+
- Auth / argument errors no longer retried as transport failures
|
|
46
|
+
- Sudo upload paths escaped; inventory cleared between `execute` loads
|
|
47
|
+
- Host timeout clock starts when work is scheduled
|
|
48
|
+
|
|
49
|
+
## [1.3.0] - 2026-07-07
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- `docker/lab` multi-distro Compose integration lab
|
|
54
|
+
|
|
55
|
+
## [1.2.0] - 2026-07-07
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Project focused on CLI gem (Web Job Console removed)
|
|
60
|
+
|
|
61
|
+
## [1.1.0] - 2026-07-07
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Exit code 1 when no hosts match
|
|
66
|
+
- Config load from task directory; `KDEPLOY_*` env overrides
|
|
67
|
+
|
|
68
|
+
## [1.0.0] - 2026-07-07
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- Initial public CLI: DSL, Runner, SSH/SCP sync, dry-run, JSON output
|
|
73
|
+
|
|
74
|
+
[Unreleased]: https://github.com/kevin197011/kdeploy/compare/v1.4.1...HEAD
|
|
75
|
+
[1.4.1]: https://github.com/kevin197011/kdeploy/compare/v1.3.5...v1.4.1
|
|
76
|
+
[1.3.5]: https://github.com/kevin197011/kdeploy/compare/v1.3.4...v1.3.5
|
|
77
|
+
[1.3.0]: https://github.com/kevin197011/kdeploy/releases/tag/v1.3.0
|
|
78
|
+
[1.2.0]: https://github.com/kevin197011/kdeploy/releases/tag/v1.2.0
|
|
79
|
+
[1.1.0]: https://github.com/kevin197011/kdeploy/releases/tag/v1.1.0
|
|
80
|
+
[1.0.0]: https://github.com/kevin197011/kdeploy/releases/tag/v1.0.0
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone git@github.com:kevin197011/kdeploy.git
|
|
9
|
+
cd kdeploy
|
|
10
|
+
bundle install
|
|
11
|
+
bundle exec rspec
|
|
12
|
+
bundle exec rubocop
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Optional multi-host lab:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cd docker/lab
|
|
19
|
+
docker compose up -d --build
|
|
20
|
+
docker compose --profile test up --build runner
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Pull requests
|
|
24
|
+
|
|
25
|
+
1. Use a feature branch.
|
|
26
|
+
2. Keep diffs focused; follow existing style (`frozen_string_literal`, RuboCop).
|
|
27
|
+
3. Add or update RSpec for behavior changes.
|
|
28
|
+
4. Use [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `docs:`, …).
|
|
29
|
+
5. Do **not** run `rake push` / `push.rb` unless you are a maintainer releasing — the default Rake task is `spec`.
|
|
30
|
+
Maintainer `rake push` runs RuboCop `-A`, RSpec, patch-bumps `lib/kdeploy/version.rb` only, then commit/pull --rebase/push (does **not** `git add .`).
|
|
31
|
+
|
|
32
|
+
## Scope
|
|
33
|
+
|
|
34
|
+
Please keep changes aligned with [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md) non-goals
|
|
35
|
+
(no agent, no Chef-style convergence engine, no Web UI).
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 kk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
- 无代理 SSH 远程执行,多主机并发
|
|
27
27
|
- Ruby DSL:主机、角色、任务;Chef 风格资源(`package`、`service`、`template`、`sync` 等)
|
|
28
28
|
- 文件上传、ERB 模板、目录同步(可选 rsync 快速路径)
|
|
29
|
-
- 试运行、JSON
|
|
29
|
+
- 试运行、JSON 输出/`--report`、`--tags` / `--extra-vars`、`wait_*` / `on_failure`、重试/超时策略
|
|
30
30
|
|
|
31
31
|
**技术栈**:Ruby、Thor、net-ssh、concurrent-ruby
|
|
32
32
|
|
|
@@ -77,17 +77,27 @@ kdeploy execute deploy.rb deploy_web # 执行
|
|
|
77
77
|
|
|
78
78
|
## 配置
|
|
79
79
|
|
|
80
|
-
项目根目录可放 `.kdeploy.yml
|
|
80
|
+
项目根目录可放 `.kdeploy.yml`(从当前目录向上查找)。重试策略示例:`retry_policy.example.json`
|
|
81
|
+
|
|
82
|
+
## 非目标
|
|
83
|
+
|
|
84
|
+
Kdeploy **不是** Ansible/Chef 收敛引擎,也 **没有** Web UI / Agent。边界见 [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md)。
|
|
85
|
+
|
|
86
|
+
## 安全注意
|
|
87
|
+
|
|
88
|
+
- 默认 `verify_host_key: accept_new`。更严用 `always`;临时 lab 才用 `never`。
|
|
89
|
+
- 优先 SSH 密钥 + NOPASSWD sudo;任务文件若含密码视为机密。
|
|
90
|
+
- 详见 [SECURITY.md](SECURITY.md)
|
|
91
|
+
|
|
92
|
+
配置示例:
|
|
81
93
|
|
|
82
94
|
```yaml
|
|
83
95
|
parallel: 10
|
|
84
96
|
ssh_timeout: 30
|
|
85
|
-
verify_host_key:
|
|
97
|
+
verify_host_key: accept_new
|
|
86
98
|
retries: 0
|
|
87
99
|
```
|
|
88
100
|
|
|
89
|
-
重试策略示例:`retry_policy.example.json`
|
|
90
|
-
|
|
91
101
|
## 示例
|
|
92
102
|
|
|
93
103
|
```bash
|
|
@@ -110,11 +120,11 @@ bundle exec rubocop
|
|
|
110
120
|
|
|
111
121
|
## 贡献
|
|
112
122
|
|
|
113
|
-
Fork → 功能分支 → 测试通过 → PR。提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/)。
|
|
123
|
+
见 [CONTRIBUTING.md](CONTRIBUTING.md)。Fork → 功能分支 → 测试通过 → PR。提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/)。
|
|
114
124
|
|
|
115
125
|
## 许可证
|
|
116
126
|
|
|
117
|
-
[MIT](
|
|
127
|
+
[MIT](LICENSE) · [变更记录](CHANGELOG.md) · [安全策略](SECURITY.md) · [贡献指南](CONTRIBUTING.md)
|
|
118
128
|
|
|
119
129
|
## 链接
|
|
120
130
|
|
data/README_EN.md
CHANGED
|
@@ -26,7 +26,7 @@ Define hosts and tasks in Ruby DSL; execute deployments and configuration across
|
|
|
26
26
|
- Agentless SSH execution with multi-host concurrency
|
|
27
27
|
- Ruby DSL: hosts, roles, tasks; Chef-style resources (`package`, `service`, `template`, `sync`, etc.)
|
|
28
28
|
- File upload, ERB templates, directory sync (optional rsync fast path)
|
|
29
|
-
- Dry-run, JSON output
|
|
29
|
+
- Dry-run, JSON output/`--report`, `--tags` / `--extra-vars`, `wait_*` / `on_failure`, retry/timeout policies
|
|
30
30
|
|
|
31
31
|
**Stack**: Ruby, Thor, net-ssh, concurrent-ruby
|
|
32
32
|
|
|
@@ -77,17 +77,28 @@ Common flags: `--limit web01`, `--parallel 5`, `--format json`, `--retries 3`. S
|
|
|
77
77
|
|
|
78
78
|
## Configuration
|
|
79
79
|
|
|
80
|
-
Place `.kdeploy.yml` in your project (walks up from CWD):
|
|
80
|
+
Place `.kdeploy.yml` in your project (walks up from CWD). Retry policy examples: `retry_policy.example.json`
|
|
81
|
+
|
|
82
|
+
## Non-goals
|
|
83
|
+
|
|
84
|
+
Kdeploy is **not** an Ansible/Chef convergence engine and has **no** Web UI / agent. See [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md).
|
|
85
|
+
|
|
86
|
+
## Security
|
|
87
|
+
|
|
88
|
+
- Default `verify_host_key` is `accept_new`. Prefer `always` for locked fleets; use `never` only for ephemeral labs.
|
|
89
|
+
- Prefer SSH keys and NOPASSWD sudo; treat task files with passwords as secrets.
|
|
90
|
+
- See [SECURITY.md](SECURITY.md)
|
|
91
|
+
|
|
92
|
+
Example:
|
|
81
93
|
|
|
82
94
|
```yaml
|
|
83
95
|
parallel: 10
|
|
84
96
|
ssh_timeout: 30
|
|
85
|
-
|
|
97
|
+
# Production:
|
|
98
|
+
verify_host_key: accept_new
|
|
86
99
|
retries: 0
|
|
87
100
|
```
|
|
88
101
|
|
|
89
|
-
Retry policy examples: `retry_policy.example.json`
|
|
90
|
-
|
|
91
102
|
## Samples
|
|
92
103
|
|
|
93
104
|
```bash
|
|
@@ -110,11 +121,11 @@ Requirements and acceptance criteria: [docs/REQUIREMENTS.md](docs/REQUIREMENTS.m
|
|
|
110
121
|
|
|
111
122
|
## Contributing
|
|
112
123
|
|
|
113
|
-
Fork → feature branch → tests pass → PR. Use [Conventional Commits](https://www.conventionalcommits.org/).
|
|
124
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Fork → feature branch → tests pass → PR. Use [Conventional Commits](https://www.conventionalcommits.org/).
|
|
114
125
|
|
|
115
126
|
## License
|
|
116
127
|
|
|
117
|
-
[MIT](
|
|
128
|
+
[MIT](LICENSE) · [Changelog](CHANGELOG.md) · [Security](SECURITY.md) · [Contributing](CONTRIBUTING.md)
|
|
118
129
|
|
|
119
130
|
## Links
|
|
120
131
|
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | --------- |
|
|
7
|
+
| 1.4.x | Yes |
|
|
8
|
+
| 1.3.x | Best-effort |
|
|
9
|
+
| < 1.3 | Best-effort only |
|
|
10
|
+
|
|
11
|
+
## Reporting a vulnerability
|
|
12
|
+
|
|
13
|
+
Please open a [GitHub Security Advisory](https://github.com/kevin197011/kdeploy/security/advisories/new)
|
|
14
|
+
or email the maintainer listed in the gemspec. Do **not** file a public issue for
|
|
15
|
+
unfixed vulnerabilities that could lead to remote code execution or credential theft.
|
|
16
|
+
|
|
17
|
+
We aim to acknowledge reports within 7 days.
|
|
18
|
+
|
|
19
|
+
## Important defaults
|
|
20
|
+
|
|
21
|
+
Kdeploy defaults `verify_host_key` to `accept_new` (first-seen host keys are
|
|
22
|
+
recorded). For locked-down fleets prefer `always`. Use `never` only on ephemeral
|
|
23
|
+
lab hosts:
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
verify_host_key: accept_new # default
|
|
27
|
+
# verify_host_key: always
|
|
28
|
+
# verify_host_key: never # labs only
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Also prefer SSH keys and NOPASSWD sudo over embedding `password` / `sudo_password`
|
|
32
|
+
in task files. Treat deploy task files as secrets if they contain credentials.
|
|
33
|
+
|
|
34
|
+
## Scope
|
|
35
|
+
|
|
36
|
+
Kdeploy executes operator-supplied shell over SSH. Malicious task files are out of
|
|
37
|
+
scope; protect who can edit and run your deploy definitions.
|
data/lib/kdeploy/cli/cli.rb
CHANGED
|
@@ -57,14 +57,19 @@ module Kdeploy
|
|
|
57
57
|
method_option :step_timeout, type: :numeric, desc: 'Per-step execution timeout seconds (default: none)'
|
|
58
58
|
method_option :retry_policy, type: :string, desc: 'Retry policy JSON to override config file'
|
|
59
59
|
method_option :retry_policy_file, type: :string, desc: 'Retry policy JSON file to override config file'
|
|
60
|
+
method_option :report, type: :string, desc: 'Write JSON execution report to PATH'
|
|
61
|
+
method_option :tags, type: :string, desc: 'Only run tasks with these tags (comma-separated)'
|
|
62
|
+
method_option :extra_vars, type: :string, desc: 'Extra vars key=value,key2=value2 (overrides DSL vars)'
|
|
60
63
|
def execute(task_file, task_name = nil)
|
|
61
64
|
@task_file_dir = File.dirname(File.expand_path(task_file))
|
|
62
65
|
Configuration.load_for_execute(task_dir: @task_file_dir)
|
|
63
66
|
show_banner_once
|
|
64
67
|
load_task_file(task_file)
|
|
68
|
+
apply_extra_vars!
|
|
65
69
|
|
|
66
70
|
tasks_to_run = determine_tasks(task_name)
|
|
67
71
|
all_results = execute_tasks(tasks_to_run)
|
|
72
|
+
write_report_file(all_results) if options[:report]
|
|
68
73
|
|
|
69
74
|
# Exit non-zero if any executed host failed or a task could not run.
|
|
70
75
|
exit 1 if any_failed?(all_results) || @task_errors
|
|
@@ -82,6 +87,8 @@ module Kdeploy
|
|
|
82
87
|
|
|
83
88
|
def load_task_file(file)
|
|
84
89
|
validate_task_file(file)
|
|
90
|
+
# Avoid leaking hosts/roles/tasks across execute invocations in one process.
|
|
91
|
+
self.class.clear_inventory!
|
|
85
92
|
# Use module_eval with top-level binding to keep heredoc compatible
|
|
86
93
|
self.class.module_eval(File.read(file), file)
|
|
87
94
|
rescue StandardError => e
|
|
@@ -125,7 +132,8 @@ module Kdeploy
|
|
|
125
132
|
end
|
|
126
133
|
|
|
127
134
|
def print_dry_run_for_host(name, config, task_name, formatter)
|
|
128
|
-
|
|
135
|
+
plan = Kdeploy::DSL.normalize_plan(self.class.kdeploy_tasks[task_name][:block].call)
|
|
136
|
+
commands = plan[:commands]
|
|
129
137
|
output = commands.map { |cmd| formatter.format_command_for_dry_run(cmd) }.join("\n")
|
|
130
138
|
title = "#{name} (#{config[:ip]})"
|
|
131
139
|
puts formatter.format_dry_run_box(title, output)
|
|
@@ -184,7 +192,7 @@ module Kdeploy
|
|
|
184
192
|
formatter.format_template_steps(steps, shown)
|
|
185
193
|
when :sync
|
|
186
194
|
formatter.format_sync_steps(steps, shown)
|
|
187
|
-
when :run
|
|
195
|
+
when :run, :wait_port, :wait_http
|
|
188
196
|
formatter.format_run_steps(steps, shown)
|
|
189
197
|
else
|
|
190
198
|
[]
|
|
@@ -228,14 +236,26 @@ module Kdeploy
|
|
|
228
236
|
end
|
|
229
237
|
|
|
230
238
|
def determine_tasks(task_name)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
239
|
+
selected =
|
|
240
|
+
if task_name
|
|
241
|
+
sym = task_name.to_sym
|
|
242
|
+
raise TaskNotFoundError, task_name unless self.class.kdeploy_tasks.key?(sym)
|
|
234
243
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
244
|
+
[sym]
|
|
245
|
+
else
|
|
246
|
+
self.class.kdeploy_tasks.keys
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
return selected unless options[:tags]
|
|
250
|
+
|
|
251
|
+
wanted = options[:tags].split(',').map(&:strip)
|
|
252
|
+
tagged = self.class.tasks_matching_tags(wanted)
|
|
253
|
+
filtered = selected.select { |name| tagged.include?(name) }
|
|
254
|
+
if filtered.empty?
|
|
255
|
+
puts Kdeploy::Banner.show_error("No tasks matched tags: #{wanted.join(', ')}")
|
|
256
|
+
@task_errors = true
|
|
238
257
|
end
|
|
258
|
+
filtered
|
|
239
259
|
end
|
|
240
260
|
|
|
241
261
|
def execute_tasks(tasks_to_run)
|
|
@@ -366,7 +386,8 @@ module Kdeploy
|
|
|
366
386
|
|
|
367
387
|
def print_dry_run_json(hosts, task_name)
|
|
368
388
|
tasks = self.class.kdeploy_tasks
|
|
369
|
-
|
|
389
|
+
plan = Kdeploy::DSL.normalize_plan(tasks[task_name][:block].call)
|
|
390
|
+
commands = plan[:commands]
|
|
370
391
|
|
|
371
392
|
payload = {
|
|
372
393
|
task: task_name.to_s,
|
|
@@ -393,6 +414,7 @@ module Kdeploy
|
|
|
393
414
|
command: step[:command],
|
|
394
415
|
duration: step[:duration]
|
|
395
416
|
}
|
|
417
|
+
out[:error] = step[:error] if step[:error]
|
|
396
418
|
|
|
397
419
|
if step[:output].is_a?(Hash)
|
|
398
420
|
out[:stdout] = step[:output][:stdout]
|
|
@@ -423,6 +445,10 @@ module Kdeploy
|
|
|
423
445
|
fast: cmd[:fast],
|
|
424
446
|
parallel: cmd[:parallel]
|
|
425
447
|
}
|
|
448
|
+
when :wait_port
|
|
449
|
+
{ type: 'wait_port', port: cmd[:port], timeout: cmd[:timeout], interval: cmd[:interval] }
|
|
450
|
+
when :wait_http
|
|
451
|
+
{ type: 'wait_http', url: cmd[:url], timeout: cmd[:timeout], interval: cmd[:interval], code: cmd[:code] }
|
|
426
452
|
else
|
|
427
453
|
{ type: cmd[:type].to_s }
|
|
428
454
|
end
|
|
@@ -489,5 +515,34 @@ module Kdeploy
|
|
|
489
515
|
rescue JSON::ParserError, Psych::SyntaxError => e
|
|
490
516
|
raise ArgumentError, "retry_policy parse error: #{e.message}"
|
|
491
517
|
end
|
|
518
|
+
|
|
519
|
+
def apply_extra_vars!
|
|
520
|
+
raw = options[:extra_vars]
|
|
521
|
+
return if raw.nil? || raw.strip.empty?
|
|
522
|
+
|
|
523
|
+
self.class.merge_extra_vars!(parse_extra_vars(raw))
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def parse_extra_vars(raw)
|
|
527
|
+
raw.split(',').each_with_object({}) do |pair, acc|
|
|
528
|
+
key, value = pair.split('=', 2)
|
|
529
|
+
raise ArgumentError, "Invalid --extra-vars pair: #{pair}" if key.nil? || value.nil?
|
|
530
|
+
|
|
531
|
+
acc[key.strip.to_sym] = value.strip
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def write_report_file(all_results)
|
|
536
|
+
path = options[:report]
|
|
537
|
+
payload = {
|
|
538
|
+
version: Kdeploy::VERSION,
|
|
539
|
+
generated_at: Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
|
540
|
+
tasks: all_results.transform_keys(&:to_s).transform_values do |task_results|
|
|
541
|
+
task_results.transform_values { |r| serialize_host_result(r) }
|
|
542
|
+
end
|
|
543
|
+
}
|
|
544
|
+
File.write(path, JSON.pretty_generate(payload))
|
|
545
|
+
warn "Report written: #{path}" unless options[:format] == 'json'
|
|
546
|
+
end
|
|
492
547
|
end
|
|
493
548
|
end
|
|
@@ -35,6 +35,9 @@ module Kdeploy
|
|
|
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
37
|
#{@pastel.dim(' --retry-on-nonzero')} Retry commands on nonzero exit status (default: false; overridden by .kdeploy.yml)
|
|
38
|
+
#{@pastel.dim(' --report PATH')} Write JSON report to PATH
|
|
39
|
+
#{@pastel.dim(' --tags TAGS')} Only run tasks matching tags
|
|
40
|
+
#{@pastel.dim(' --extra-vars K=V,...')} Set/override DSL vars
|
|
38
41
|
#{@pastel.dim(' --timeout SECONDS')} Per-host execution timeout seconds (default: none; overridden by .kdeploy.yml)
|
|
39
42
|
#{@pastel.dim(' --step-timeout SECONDS')} Per-step execution timeout seconds (default: none; overridden by .kdeploy.yml)
|
|
40
43
|
#{@pastel.dim(' --retry-policy JSON')} Retry policy JSON (overrides .kdeploy.yml)
|
|
@@ -68,6 +71,9 @@ module Kdeploy
|
|
|
68
71
|
#{@pastel.dim('# Machine-readable output')}
|
|
69
72
|
#{@pastel.bright_cyan('kdeploy execute deploy.rb deploy_web --format json --no-banner')}
|
|
70
73
|
|
|
74
|
+
#{@pastel.dim('# Tags, vars, and JSON report')}
|
|
75
|
+
#{@pastel.bright_cyan('kdeploy execute deploy.rb --tags web --extra-vars app=demo --report out.json')}
|
|
76
|
+
|
|
71
77
|
#{@pastel.dim('# Retry transient network failures')}
|
|
72
78
|
#{@pastel.bright_cyan('kdeploy execute deploy.rb deploy_web --retries 3 --retry-delay 1')}
|
|
73
79
|
EXAMPLES
|
|
@@ -7,7 +7,7 @@ module Kdeploy
|
|
|
7
7
|
class Configuration
|
|
8
8
|
DEFAULT_PARALLEL = 10
|
|
9
9
|
DEFAULT_SSH_TIMEOUT = 30
|
|
10
|
-
DEFAULT_VERIFY_HOST_KEY = :
|
|
10
|
+
DEFAULT_VERIFY_HOST_KEY = :accept_new
|
|
11
11
|
DEFAULT_RETRIES = 0
|
|
12
12
|
DEFAULT_RETRY_DELAY = 1
|
|
13
13
|
DEFAULT_HOST_TIMEOUT = nil
|
|
@@ -49,7 +49,12 @@ module Kdeploy
|
|
|
49
49
|
config_path ||= find_config_file
|
|
50
50
|
return unless config_path && File.exist?(config_path)
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
raw = File.read(config_path)
|
|
53
|
+
config = begin
|
|
54
|
+
YAML.safe_load(raw, permitted_classes: [Symbol])
|
|
55
|
+
rescue ArgumentError
|
|
56
|
+
YAML.safe_load(raw, [Symbol])
|
|
57
|
+
end
|
|
53
58
|
apply_config(config)
|
|
54
59
|
rescue StandardError => e
|
|
55
60
|
warn "Warning: Failed to load config from #{config_path}: #{e.message}"
|