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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2e8622dd69aeb8ad48fb4e9b5d0212345698f81b78f042d4dfa761bdf0c3890
4
- data.tar.gz: b579bf0a5ad0a3d795249f4e8d0048366120f5a8f857f95a29901c78266b28f2
3
+ metadata.gz: 6757c9c4e28fdbf88436ead17a364fc8dcb7fc2d4fda107654fd278c0d5229c3
4
+ data.tar.gz: a727b09657a6b79ab821c2a2f8f899c1d09b248faf659ff288306514c07d0a68
5
5
  SHA512:
6
- metadata.gz: 2e18324d1c852f300873ea0bf02fadd159fd70bf4637f716590e1e0435d92d5dc1a1580e5eed4f552cc607cf2aeef5549df544a8f07f1340faff5cc87724ecc3
7
- data.tar.gz: b61f9d0812d4bde92d8b5bf86d804bc9d9f2b8991b7f3b7c308909c74696c4ecfec333dd1ef9b14ae9da57c043cf91a35b9ee74d62eaeea6bd17398e015222c6
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.0.gem
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 提交和候选制品校验;发布需另行明确授权。
@@ -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("配置:#{@options.config}")
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} 参数或报告错误:#{report_diagnostic(error)}"
63
+ diagnostic "#{@label} argument or report error: #{report_diagnostic(error)}"
64
64
  2
65
65
  rescue Error, Configuration::Error => error
66
- diagnostic "#{@label} 参数、依赖或报告错误:#{error.message};使用 --help 查看参数"
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} 参数、依赖或报告错误:#{error.class.name};使用 --help 查看参数"
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} 清单或执行失败:#{@failure_code.call(error)}(#{error.class.name})"
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} 摘要报告错误:#{outcome.finalization_error}"
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} 参数或报告错误:#{failure[:detail]}"
94
+ diagnostic "#{@label} argument or report error: #{failure[:detail]}"
95
95
  else
96
- diagnostic "#{@label} 清单或执行失败:#{failure[:error_type]};已采集结果见批次报告"
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 "设备:#{value.size} 台;未建立设备连接"
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
@@ -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, "不接受位置参数" unless rest.empty?
21
+ raise ArgumentError, "Positional arguments are not accepted" unless rest.empty?
22
22
  @values
23
23
  end
24
24
 
@@ -4,34 +4,34 @@ module CtxCli
4
4
  # 普通参数分组只登记语法;不加载配置、不执行操作、不拥有领域校验。
5
5
  module Options
6
6
  def self.initialization(parser, values)
7
- parser.on("--init", "创建缺失配置和环境模板后退出;已有文件不覆盖") { values[:init] = true }
8
- parser.on("--interactive", "缺少配置时询问是否创建;要求真实终端") { values[:interactive] = true }
9
- parser.on("--non-interactive", "禁用问答;创建文件须同时指定 --init") { values[:interactive] = false }
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 输出 JSON;默认显示可读摘要") { values[:json] = true }
14
- parser.on("--quiet", "--no-progress", "关闭 stderr 进度,可配合 --json") { values[quiet] = true }
15
- parser.on("--verbose", "显示逐台结果及安全命令事件") { values[:verbose] = true }
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", "查看选项帮助") { values[:help] = true }
20
- parser.on("--help-all", "查看全部选项、路径与环境约定") { values[:help_all] = true }
21
- parser.on("--examples", "查看常用命令范例,不执行任务") { values[:examples] = true }
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: "设备并发数", &store)
26
- parser.on("--config FILE", "显式 YAML 最后叠加于自动发现的配置") { |value| store.call(:config, value) }
27
- parser.on("--env-file FILE", "显式环境文件最后叠加;进程环境优先") { |value| store.call(:env_file, value) }
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", "按设备保存报告的目录") { |value| store.call(:directory, value) }
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, "列表不能有空项" if values.any?(&:empty?)
34
+ raise ArgumentError, "Lists must not contain empty entries" if values.any?(&:empty?)
35
35
  values
36
36
  end
37
37
  end
@@ -14,7 +14,7 @@ module CtxCli
14
14
 
15
15
  # 最后的错误流失败不能覆盖原本的失败或取消状态。
16
16
  def diagnostic(message, exception: nil)
17
- @error.puts(exception ? "#{message}(#{exception.class.name})" : message)
17
+ @error.puts(exception ? "#{message} (#{exception.class.name})" : message)
18
18
  rescue StandardError
19
19
  nil
20
20
  end
@@ -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, "需要批次目录" unless options[:batch]
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, "无设备报告" if paths.empty?
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("建议生成失败(#{error.class.name});请检查输入结构、目录权限和 --help;未执行设备变更。")
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
 
@@ -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 "未知命令或多余参数;使用 #{@name} --help 查看用法"
24
+ @error.puts "Unknown command or extra arguments; use #{@name} --help for usage"
25
25
  2
26
26
  end
27
27
  end
@@ -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, "密码提示需要交互终端;自动化任务可使用环境变量或 --stdin-credentials" unless self.class.tty?(@input)
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, "密码不能为空" if value.nil? || value.empty?
35
+ raise ArgumentError, "Password must not be empty" if value.nil? || value.empty?
36
36
  value
37
37
  end
38
38
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CtxCli
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
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.0
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: '3.0'
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: '3.0'
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