ctx_cli 0.2.0 → 0.2.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 +5 -0
- data/README.md +1 -1
- data/lib/ctx_cli/collection.rb +14 -14
- data/lib/ctx_cli/command.rb +1 -1
- data/lib/ctx_cli/options.rb +14 -14
- data/lib/ctx_cli/output.rb +1 -1
- data/lib/ctx_cli/review.rb +4 -4
- data/lib/ctx_cli/router.rb +1 -1
- data/lib/ctx_cli/terminal.rb +4 -4
- data/lib/ctx_cli/version.rb +1 -1
- metadata +3 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6757c9c4e28fdbf88436ead17a364fc8dcb7fc2d4fda107654fd278c0d5229c3
|
|
4
|
+
data.tar.gz: a727b09657a6b79ab821c2a2f8f899c1d09b248faf659ff288306514c07d0a68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0d80afb9a473f4bceff0dc6f0cfbb7b78d2987b8aed673c4b230e1c05086bdbc9f2fa950ee1f54dc95cbfe487f803178ecace433d40ac758257e7b0633c726e
|
|
7
|
+
data.tar.gz: ee8af08341391087d93c40df85d61b486346fcfef8c92ee8b675704749e4ad3e4dcededc8126edadb7d52fc1d82190725fa1e1774c6604cd36124c6c9f938c6e
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.1 (2026-10-23)
|
|
6
|
+
|
|
7
|
+
- Use English command help, prompts and collection/review diagnostics.
|
|
8
|
+
- Allow compatible dotenv versions without an upper version bound.
|
|
9
|
+
|
|
5
10
|
## 0.2.0 (2026-10-23)
|
|
6
11
|
|
|
7
12
|
- Rename Initialization to Initializer and Application to Router; remove the old constants and require paths in the 0.2.0 candidate.
|
data/README.md
CHANGED
|
@@ -73,7 +73,7 @@ Options 协作者沿用现有入口协议:`informational?`、`validate_interac
|
|
|
73
73
|
bundle install
|
|
74
74
|
bundle exec rake
|
|
75
75
|
bundle exec rake build
|
|
76
|
-
ruby script/verify_package.rb pkg/ctx_cli-0.2.
|
|
76
|
+
ruby script/verify_package.rb pkg/ctx_cli-0.2.1.gem
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
默认任务包括 RSpec、RuboCop 和离线示例。安装验证在独立 GEM_HOME/GEM_PATH 下运行普通与最小 Bundler 应用,检查实际加载来源。CI 参照 ctx_parser 的固定 action 提交和候选制品校验;发布需另行明确授权。
|
data/lib/ctx_cli/collection.rb
CHANGED
|
@@ -33,13 +33,13 @@ module CtxCli
|
|
|
33
33
|
return 0 if @options.missing?(input: @input, error: @error)
|
|
34
34
|
@options.load
|
|
35
35
|
@progress = @progress_factory.call(@options)
|
|
36
|
-
@progress.notice("
|
|
36
|
+
@progress.notice("Configuration: #{@options.config}")
|
|
37
37
|
return output(@options.public_values.merge("sources" => @options.sources)) if @options.flags[:show_config]
|
|
38
38
|
@devices, @inventory_total = @adapter.inventory(@options, progress: @progress)
|
|
39
39
|
devices = @devices
|
|
40
40
|
return output(devices) if @options.flags[:inventory_only]
|
|
41
41
|
if devices.empty?
|
|
42
|
-
@progress.notice("
|
|
42
|
+
@progress.notice("No devices matched; no connections opened")
|
|
43
43
|
@output.json({ status: :no_devices, total: 0 }, pretty: false) if @options.flags[:json]
|
|
44
44
|
return 1
|
|
45
45
|
end
|
|
@@ -47,7 +47,7 @@ module CtxCli
|
|
|
47
47
|
@store = @adapter.report(@options)
|
|
48
48
|
@batch = @batch_class.new(report: @store, progress: @progress, requested: devices)
|
|
49
49
|
outcome = @batch.run(present: ->(summary) { @output.json(summary) if @options.flags[:json] }) do
|
|
50
|
-
@progress.plan(devices: devices, total: @inventory_total, parallel: @options.values.fetch("parallel"), label: "#{@label}
|
|
50
|
+
@progress.plan(devices: devices, total: @inventory_total, parallel: @options.values.fetch("parallel"), label: "#{@label} read-only collection")
|
|
51
51
|
@progress.with_updates do
|
|
52
52
|
@adapter.execute(devices, @options) do |device|
|
|
53
53
|
@batch.collect(device) { @adapter.collect(device, @options, context: context, progress: @progress) }
|
|
@@ -57,19 +57,19 @@ module CtxCli
|
|
|
57
57
|
end
|
|
58
58
|
completion(outcome)
|
|
59
59
|
rescue Interrupt
|
|
60
|
-
diagnostic "
|
|
60
|
+
diagnostic "Collection cancelled; saved device reports retained; check report status for unsaved results"
|
|
61
61
|
130
|
|
62
62
|
rescue SystemCallError, IOError => error
|
|
63
|
-
diagnostic "#{@label}
|
|
63
|
+
diagnostic "#{@label} argument or report error: #{report_diagnostic(error)}"
|
|
64
64
|
2
|
|
65
65
|
rescue Error, Configuration::Error => error
|
|
66
|
-
diagnostic "#{@label}
|
|
66
|
+
diagnostic "#{@label} argument, dependency or report error: #{error.message}; use --help for options"
|
|
67
67
|
2
|
|
68
68
|
rescue OptionParser::ParseError, ArgumentError, LoadError => error
|
|
69
|
-
diagnostic "#{@label}
|
|
69
|
+
diagnostic "#{@label} argument, dependency or report error: #{error.class.name}; use --help for options"
|
|
70
70
|
2
|
|
71
71
|
rescue StandardError => error
|
|
72
|
-
diagnostic "#{@label}
|
|
72
|
+
diagnostic "#{@label} inventory or execution failed: #{@failure_code.call(error)} (#{error.class.name})"
|
|
73
73
|
2
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -82,18 +82,18 @@ module CtxCli
|
|
|
82
82
|
# Runner 决定批次结论;CLI 仅呈现安全诊断并映射退出码。
|
|
83
83
|
def completion(outcome)
|
|
84
84
|
if outcome.finalization_error
|
|
85
|
-
diagnostic "#{@label}
|
|
85
|
+
diagnostic "#{@label} summary report error: #{outcome.finalization_error}"
|
|
86
86
|
end
|
|
87
87
|
if outcome.recovery_failed
|
|
88
|
-
diagnostic "
|
|
88
|
+
diagnostic "Summary recovery is unconfirmed; check the exit code and report directory. An earlier summary does not confirm this run succeeded."
|
|
89
89
|
end
|
|
90
90
|
if outcome.status == :interrupted
|
|
91
|
-
diagnostic "
|
|
91
|
+
diagnostic "Collection cancelled; saved device reports retained; check report status for unsaved results"
|
|
92
92
|
elsif (failure = outcome.failure)
|
|
93
93
|
if failure[:kind] == :report
|
|
94
|
-
diagnostic "#{@label}
|
|
94
|
+
diagnostic "#{@label} argument or report error: #{failure[:detail]}"
|
|
95
95
|
else
|
|
96
|
-
diagnostic "#{@label}
|
|
96
|
+
diagnostic "#{@label} inventory or execution failed: #{failure[:error_type]}; see the batch report for collected results"
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
{ complete: 0, incomplete: 1, failed: 2, interrupted: 130 }.fetch(outcome.status)
|
|
@@ -115,7 +115,7 @@ module CtxCli
|
|
|
115
115
|
@output.json(value)
|
|
116
116
|
else
|
|
117
117
|
value.each { |device| @out.puts "#{device.fetch('host')}\t#{device.fetch('name')}\t#{device['model']}" }
|
|
118
|
-
@out.puts "
|
|
118
|
+
@out.puts "Devices: #{value.size}; no device connections opened"
|
|
119
119
|
end
|
|
120
120
|
value.is_a?(Array) && value.empty? ? 1 : 0
|
|
121
121
|
end
|
data/lib/ctx_cli/command.rb
CHANGED
|
@@ -18,7 +18,7 @@ module CtxCli
|
|
|
18
18
|
@parser.banner = @banner
|
|
19
19
|
@configure&.call(@parser, @values)
|
|
20
20
|
rest = @parser.parse(argv.dup)
|
|
21
|
-
raise ArgumentError, "
|
|
21
|
+
raise ArgumentError, "Positional arguments are not accepted" unless rest.empty?
|
|
22
22
|
@values
|
|
23
23
|
end
|
|
24
24
|
|
data/lib/ctx_cli/options.rb
CHANGED
|
@@ -4,34 +4,34 @@ module CtxCli
|
|
|
4
4
|
# 普通参数分组只登记语法;不加载配置、不执行操作、不拥有领域校验。
|
|
5
5
|
module Options
|
|
6
6
|
def self.initialization(parser, values)
|
|
7
|
-
parser.on("--init", "
|
|
8
|
-
parser.on("--interactive", "
|
|
9
|
-
parser.on("--non-interactive", "
|
|
7
|
+
parser.on("--init", "Create missing configuration and environment templates, then exit; preserve existing files") { values[:init] = true }
|
|
8
|
+
parser.on("--interactive", "Prompt to create missing configuration; requires an interactive terminal") { values[:interactive] = true }
|
|
9
|
+
parser.on("--non-interactive", "Disable prompts; specify --init to create files") { values[:interactive] = false }
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def self.output(parser, values, quiet: :quiet)
|
|
13
|
-
parser.on("--json", "stdout
|
|
14
|
-
parser.on("--quiet", "--no-progress", "
|
|
15
|
-
parser.on("--verbose", "
|
|
13
|
+
parser.on("--json", "Write JSON to stdout; defaults to a readable summary") { values[:json] = true }
|
|
14
|
+
parser.on("--quiet", "--no-progress", "Disable progress on stderr; can be combined with --json") { values[quiet] = true }
|
|
15
|
+
parser.on("--verbose", "Show per-device results and safe command events") { values[:verbose] = true }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def self.information(parser, values)
|
|
19
|
-
parser.on("-h", "--help", "
|
|
20
|
-
parser.on("--help-all", "
|
|
21
|
-
parser.on("--examples", "
|
|
19
|
+
parser.on("-h", "--help", "Show option help") { values[:help] = true }
|
|
20
|
+
parser.on("--help-all", "Show all options, paths and environment conventions") { values[:help_all] = true }
|
|
21
|
+
parser.on("--examples", "Show common command examples without executing tasks") { values[:examples] = true }
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# 值转换留在宿主边界,允许不同入口沿用原有存储格式。
|
|
25
|
-
def self.configuration(parser, parallel_help: "
|
|
26
|
-
parser.on("--config FILE", "
|
|
27
|
-
parser.on("--env-file FILE", "
|
|
25
|
+
def self.configuration(parser, parallel_help: "Device concurrency", &store)
|
|
26
|
+
parser.on("--config FILE", "Overlay explicit YAML after automatically discovered configuration") { |value| store.call(:config, value) }
|
|
27
|
+
parser.on("--env-file FILE", "Load explicit environment files last; process environment takes precedence") { |value| store.call(:env_file, value) }
|
|
28
28
|
parser.on("-p", "--parallel N", Integer, parallel_help) { |value| store.call(:parallel, value) }
|
|
29
|
-
parser.on("--directory DIR", "
|
|
29
|
+
parser.on("--directory DIR", "Directory for per-device reports") { |value| store.call(:directory, value) }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def self.list(value)
|
|
33
33
|
values = value.split(",", -1).map(&:strip)
|
|
34
|
-
raise ArgumentError, "
|
|
34
|
+
raise ArgumentError, "Lists must not contain empty entries" if values.any?(&:empty?)
|
|
35
35
|
values
|
|
36
36
|
end
|
|
37
37
|
end
|
data/lib/ctx_cli/output.rb
CHANGED
|
@@ -14,7 +14,7 @@ module CtxCli
|
|
|
14
14
|
|
|
15
15
|
# 最后的错误流失败不能覆盖原本的失败或取消状态。
|
|
16
16
|
def diagnostic(message, exception: nil)
|
|
17
|
-
@error.puts(exception ? "#{message}
|
|
17
|
+
@error.puts(exception ? "#{message} (#{exception.class.name})" : message)
|
|
18
18
|
rescue StandardError
|
|
19
19
|
nil
|
|
20
20
|
end
|
data/lib/ctx_cli/review.rb
CHANGED
|
@@ -23,10 +23,10 @@ module CtxCli
|
|
|
23
23
|
def run(argv)
|
|
24
24
|
options = @command.parse(argv)
|
|
25
25
|
return 0 if @command.informational?
|
|
26
|
-
raise ArgumentError, "
|
|
26
|
+
raise ArgumentError, "A batch directory is required" unless options[:batch]
|
|
27
27
|
batch = File.expand_path(options[:batch])
|
|
28
28
|
paths = source_paths(File.join(batch, @directory))
|
|
29
|
-
raise ArgumentError, "
|
|
29
|
+
raise ArgumentError, "No device reports found" if paths.empty?
|
|
30
30
|
remaining = @max_total_bytes
|
|
31
31
|
sources = paths.map do |path|
|
|
32
32
|
raise ArgumentError, "review byte budget exceeded" unless remaining.positive?
|
|
@@ -38,10 +38,10 @@ module CtxCli
|
|
|
38
38
|
@output.text(@adapter.publish(sources, options, batch: batch, context: context))
|
|
39
39
|
0
|
|
40
40
|
rescue Interrupt
|
|
41
|
-
@output.diagnostic("
|
|
41
|
+
@output.diagnostic("Review cancelled; existing reports retained; no device changes executed.")
|
|
42
42
|
130
|
|
43
43
|
rescue StandardError => error
|
|
44
|
-
@output.diagnostic("
|
|
44
|
+
@output.diagnostic("Review failed (#{error.class.name}); check the input structure, directory permissions and --help; no device changes executed.")
|
|
45
45
|
2
|
|
46
46
|
end
|
|
47
47
|
|
data/lib/ctx_cli/router.rb
CHANGED
|
@@ -21,7 +21,7 @@ module CtxCli
|
|
|
21
21
|
return 0
|
|
22
22
|
end
|
|
23
23
|
return @commands.fetch(command).call(arguments) if @commands.key?(command)
|
|
24
|
-
@error.puts "
|
|
24
|
+
@error.puts "Unknown command or extra arguments; use #{@name} --help for usage"
|
|
25
25
|
2
|
|
26
26
|
end
|
|
27
27
|
end
|
data/lib/ctx_cli/terminal.rb
CHANGED
|
@@ -10,7 +10,7 @@ module CtxCli
|
|
|
10
10
|
def self.tty?(*streams) = streams.all? { |stream| stream.respond_to?(:tty?) && stream.tty? }
|
|
11
11
|
def tty?(*others) = self.class.tty?(@input, @out, *others)
|
|
12
12
|
|
|
13
|
-
def require_tty!(*others, message: "
|
|
13
|
+
def require_tty!(*others, message: "An interactive terminal is required")
|
|
14
14
|
raise ArgumentError, message unless tty?(*others)
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -26,13 +26,13 @@ module CtxCli
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def password(label:)
|
|
29
|
-
raise ArgumentError, "
|
|
29
|
+
raise ArgumentError, "Password prompts require an interactive terminal; use environment variables or --stdin-credentials for automation" unless self.class.tty?(@input)
|
|
30
30
|
require "io/console"
|
|
31
|
-
@out.print "#{label}
|
|
31
|
+
@out.print "#{label}: "
|
|
32
32
|
@out.flush
|
|
33
33
|
value = @input.noecho { |terminal| terminal.gets("\n") }&.chomp("\n")
|
|
34
34
|
@out.puts
|
|
35
|
-
raise ArgumentError, "
|
|
35
|
+
raise ArgumentError, "Password must not be empty" if value.nil? || value.empty?
|
|
36
36
|
value
|
|
37
37
|
end
|
|
38
38
|
end
|
data/lib/ctx_cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ctx_cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gatework contributors
|
|
@@ -15,20 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
19
|
-
- - "<"
|
|
20
|
-
- !ruby/object:Gem::Version
|
|
21
|
-
version: '4'
|
|
18
|
+
version: '0'
|
|
22
19
|
type: :runtime
|
|
23
20
|
prerelease: false
|
|
24
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
22
|
requirements:
|
|
26
23
|
- - ">="
|
|
27
24
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: '
|
|
29
|
-
- - "<"
|
|
30
|
-
- !ruby/object:Gem::Version
|
|
31
|
-
version: '4'
|
|
25
|
+
version: '0'
|
|
32
26
|
- !ruby/object:Gem::Dependency
|
|
33
27
|
name: fileutils
|
|
34
28
|
requirement: !ruby/object:Gem::Requirement
|