vxlan 0.4.1 → 0.5.0

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: 203d6c19eed76f37e033ba015e36a39953de0ba63dc5fd4130873a83f1e097c3
4
- data.tar.gz: bb21ff1519721b7ee99cdedc5af2a3ac34c8542563c06ce9d3cd1328c31c4d6f
3
+ metadata.gz: 3711abc03628af1231bcd4698cff27e399db8b596a4d5e6ec46712bfc43f38fb
4
+ data.tar.gz: b57592e473ca59bfcf3c04a8abf6ed92a34bbd05a2d7d9e683be005437860f6d
5
5
  SHA512:
6
- metadata.gz: aa2702cbc3fc196d77c7cb4ac24aca422f4e03eaf6ac4967d07b946e762ff774b9359d24deae4430ca9e7a8644992909e00b185c48d14b575d30bbeb037f381c
7
- data.tar.gz: fea09bbf0ea945b4c33913f759aad4e4181bd4da4148593ade4e177a18939938ef7c0b327bbce9da0b3ac18e95d9bee9ebced5afd2014b4c35978bd9b80fb7c0
6
+ metadata.gz: '08f9a10991fb96ebaf09c2e30f4893a056576dcc48d1ad3471021b2c1c41efaab326e18873f008c42153036a545db3ba9be5f427b2e75aa778a6fa54f849795b'
7
+ data.tar.gz: 505286b2466afe7bbe7460680c4c3f791a476f6b5e5b3848ae4a2105c8f2003f92d4005cf16fbed1a581ebd1b711bb7882ed32ad4ff6db195fed2856243336e3
data/CHANGELOG.md CHANGED
@@ -2,17 +2,21 @@
2
2
 
3
3
  Changes to the public contract are recorded here. Versions follow Semantic Versioning.
4
4
 
5
- ## 0.4.0 (2026-10-22)
5
+ ## Unreleased
6
6
 
7
- - Delegate batch completion and cancellation/failure precedence to CtxRunner::Batch, keeping domain review and CLI presentation explicit.
7
+ ## 0.5.0 (2026-10-23)
8
8
 
9
- - Centralize inventory selection and CLI filters in Net::Connector::Inventory; add vendor/model inclusion, exclusion and matching modes.
9
+ - Consume immutable string-keyed Runner summary documents throughout reports and progress callbacks.
10
+ - Use English diagnostics, CLI help, change plans and Fabric review reports.
11
+ - Require Runner 0.2, net-connector 0.18 and the coordinated command receipt protocol.
10
12
 
13
+ ## 0.4.2 (2026-10-23)
11
14
 
12
- - Delegate shared collection, evidence and execution contracts to ctx_runner.
13
- - Preserve domain parsing, change policies, CLI layouts and connection ownership.
14
-
15
- ## Unreleased
15
+ - Migrate shared CLI entrypoints to ctx_cli 0.2.0 names without legacy aliases.
16
+ - Delegate offline review entrypoint orchestration to ctx_cli and bound external evidence, plan, checksum and Fabric policy reads.
17
+ - Share offline evidence validation and JSON conversion with Runner; reject missing snapshot target identities.
18
+ - Use explicit field separators and align CLI dependency floors with shared inventory support.
19
+ - Delegate shared command dispatch, option groups, initialization, terminal confirmation, offline IO and collection entrypoint flow to ctx_cli; retain domain and Runner responsibilities.
16
20
 
17
21
  ## 0.4.1 (2026-10-22)
18
22
 
@@ -21,6 +25,16 @@ Changes to the public contract are recorded here. Versions follow Semantic Versi
21
25
 
22
26
  - Delegate batch completion and cancellation/failure precedence to CtxRunner::Batch, keeping domain review and CLI presentation explicit.
23
27
 
28
+ ## 0.4.0 (2026-10-22)
29
+
30
+ - Delegate batch completion and cancellation/failure precedence to CtxRunner::Batch, keeping domain review and CLI presentation explicit.
31
+
32
+ - Centralize inventory selection and CLI filters in Net::Connector::Inventory; add vendor/model inclusion, exclusion and matching modes.
33
+
34
+
35
+ - Delegate shared collection, evidence and execution contracts to ctx_runner.
36
+ - Preserve domain parsing, change policies, CLI layouts and connection ownership.
37
+
24
38
  ## 0.3.1 (2026-10-21)
25
39
 
26
40
  - Index Fabric address-group evidence by referenced object, preserving host order and missing dependencies without repeatedly searching every host's reference list.
data/README-zh.md CHANGED
@@ -59,7 +59,7 @@ observation.records.each { |record| p record }
59
59
  ```sh
60
60
  gem install vxlan
61
61
  # 联网采集与读取 .env 所需的可选依赖:
62
- gem install net-connector -v '~> 0.16.0'
62
+ gem install net-connector -v '~> 0.18.0'
63
63
  vxlan --help
64
64
  vxlan collect --init --non-interactive
65
65
  vxlan collect --inventory-only
data/README.md CHANGED
@@ -59,7 +59,7 @@ The gem installs `vxlan`; its implementation lives in `lib/vxlan/cli/`. The scri
59
59
  ```sh
60
60
  gem install vxlan
61
61
  # Optional dependencies for live collection and .env files:
62
- gem install net-connector -v '~> 0.16.0'
62
+ gem install net-connector -v '~> 0.18.0'
63
63
  vxlan --help
64
64
  vxlan collect --init --non-interactive
65
65
  vxlan collect --inventory-only
@@ -136,3 +136,5 @@ MIT. See [LICENSE](LICENSE).
136
136
  `vxlan collect` 默认输出逐设备诊断,使用 `--scripts` 可选导出配置建议;`vxlan review --batch DIR` 可离线补生成既有报告的候选配置、条件回退和只读验收命令。导出不会执行变更;独立 `apply --plan FILE --item ID` 必须在真实终端明确确认,配置漂移或失败即停止。详见[修复建议说明](docs/remediation.md)。 业务分层、会话所有权与执行边界见[架构说明](docs/architecture-zh.md)。
137
137
 
138
138
  Review output follows the net-connector topology layout: `summary.txt`, `summary.json`, `devices/*.txt`, optional `scripts/*.cfg`, and a checksummed `plan.json`. Generated reports contain no Markdown. Select exactly one change with `vxlan apply --plan plan.json --item ID`; interactive confirmation remains mandatory.
139
+
140
+ Offline review requires `snapshot.target.host` to match the canonical device host. Reports without target identity must be collected again; the unbound legacy format is no longer accepted.
data/bin/README.md CHANGED
@@ -17,7 +17,7 @@ bundle exec ruby examples/offline.rb
17
17
 
18
18
  `bin/` 的公开内容是 `vxlan` 与本说明;现场 `vxlan.yml`、`.env`、`vxlans/` 由用户按需创建,不进入版本库或安装包。配置和凭据模板统一位于 `lib/vxlan/cli/templates/`,使用 `--init` 生成,不维护重复模板。
19
19
 
20
- 离线解析和帮助不加载 net-connector。采集配置与联网入口可选依赖 `net-connector ~> 0.17.1`(已包含 `.env` 加载依赖 dotenv)。本仓库 Gemfile 的 `examples` 组已声明连接器,不进入 vxlan.gemspec 的运行依赖。只安装 vxlan gem 的宿主需要自行声明相应可选依赖。
20
+ 离线解析和帮助不加载 net-connector。采集配置与联网入口可选依赖 `net-connector ~> 0.18.0`(已包含 `.env` 加载依赖 dotenv)。本仓库 Gemfile 的 `examples` 组已声明连接器,不进入 vxlan.gemspec 的运行依赖。只安装 vxlan gem 的宿主需要自行声明相应可选依赖。
21
21
 
22
22
  ## 正式命令
23
23
 
@@ -25,7 +25,7 @@ bundle exec ruby examples/offline.rb
25
25
 
26
26
  ```sh
27
27
  gem install vxlan
28
- gem install net-connector -v '~> 0.17.1'
28
+ gem install net-connector -v '~> 0.18.0'
29
29
  vxlan --help
30
30
  vxlan collect --init --non-interactive
31
31
  vxlan collect --inventory-only
@@ -0,0 +1,138 @@
1
+ # ctx_cli Implementation Plan
2
+
3
+ > **For agentic workers:** Use executing-plans to implement this plan task-by-task. Preserve existing uncommitted changes; no commits or publication are authorized.
4
+
5
+ **Goal:** Extract shared CLI responsibilities into ctx_cli and migrate existing consumers without adding a baseline command.
6
+
7
+ **Architecture:** Small Ruby objects composed by domain adapters. ctx_cli has no dependency on net-connector or ctx_runner; existing connector configuration policies and Runner business processes remain their owners' responsibilities.
8
+
9
+ **Tech Stack:** Ruby >= 3.4, OptionParser, YAML, dotenv, RSpec, RuboCop Omakase. Development Ruby 4.0.6.
10
+
11
+ **Spec:** ../specs/2026-10-22-ctx-cli-design.md
12
+
13
+ ## Global Constraints
14
+
15
+ - Preserve current CLI contracts, configuration discovery, precedence and source-relative paths.
16
+ - No new baseline executable; coverage.rb is the existing script integration.
17
+ - No automatic network I/O, ENV mutation, input consumption or directory creation on require.
18
+ - Preserve vendor parser names/layout, unrelated WIP, and existing ctx_runner/Inventory work.
19
+ - Chinese code comments, English RSpec/Changelog, explicit require and package allowlist.
20
+ - Keep net-connector Ruby >= 3.4; no dependency cycle or implicit sibling checkout loading.
21
+
22
+ ## Review Focus
23
+
24
+ - Reused CLI objects must start a fresh batch and leave previous reports intact after preflight failure.
25
+ - Output or directory-sync failure after an external effect must not turn into success or replay.
26
+ - Missing/false/nil and overridden invalid values must retain existing configuration semantics.
27
+ - Non-TTY input and caller-owned IO must not be consumed/closed by help or preview.
28
+ - Installed packages must exercise actual candidate dependencies rather than workspace load paths.
29
+
30
+ ### Task 1: Configuration and initialization
31
+
32
+ **Files:** Create ctx_cli gem engineering files; lib/ctx_cli/{version,configuration,initialization}.rb; spec/{configuration,initialization}_spec.rb. Modify net-connector-trunk/lib/net/connector/config.rb and netdisco/config/initializer.rb; Gemfile/gemspec.
33
+
34
+ **Interfaces:** Configuration.new(name:, config_env:, namespace:, names:, common_keys:, **existing_discovery_keywords), #load_environment, #load, #sources and existing Config readers; .merge/.copy/.freeze_tree/.dump. Initialization.new(prefix:).create(path:, contents:) returns :created/:exists and rejects nonregular existing targets.
35
+
36
+ - [x] Add generic-policy configuration tests, run RED for missing ctx_cli, implement extracted source reader with explicit host policy.
37
+ - [x] Add private-mode/no-overwrite/symlink/sync-failure initialization tests, implement existing durable publisher once.
38
+ - [x] Replace connector Config with policy adapter and initializer publisher with delegation; existing config and backup tests stay green.
39
+
40
+ ### Task 2: Command, terminal, input/output and application
41
+
42
+ **Files:** Create lib/ctx_cli/{application,command,options,terminal,input,output}.rb and focused specs/examples.
43
+
44
+ **Interfaces:** Application.new(name:, version:, usage:, commands:, out:, error:).run(argv); Command.new(banner:, defaults:, out:) { |parser, values| ... }.parse(argv) and #informational?(examples:, details:); Options group registration; Terminal.new(input:, out:).tty?/#confirm(token:, prompt:)/#question(prompt)/#password(label:); Input.read(path:, input:); Output.new(out:, error:).json/.diagnostic.
45
+
46
+ - [x] Pin lazy help/version, no argv mutation, no IO ownership, exact confirmation, safe diagnostic and stdout failure behavior before implementing.
47
+ - [x] Keep the Command API simple: construct parser/value snapshot, configure through a block, parse through #parse, informational? short-circuit; domain execution and error policy remain explicit.
48
+
49
+ ### Task 3: Existing terminal entrypoints
50
+
51
+ **Files:** VXLAN/Wireless cli.rb and cli/{options,parse,apply}.rb; backup CLI options/setup and config/initializer; baseline/script/coverage.rb; consumer Gemfiles/gemspecs.
52
+
53
+ **Interfaces:** Consume Tasks 1–2; preserve public constructor/run signatures.
54
+
55
+ - [x] Register shared options once; replace initialization and interaction copies; keep option descriptions and domain validation in host where necessary.
56
+ - [x] Share parse input/output and informational flow; retain domain dimension/platform dispatch.
57
+ - [x] Share apply terminal gate and exact confirmation; retain plan shape, hash, item and operation lease differences.
58
+ - [x] Move coverage script argument/output boundary to Command, keeping []/--check behavior and document bytes.
59
+ - [x] Run existing CLI suites and actual checkout help/examples; only add regression tests for uncovered changed boundaries.
60
+
61
+ ### Task 4: Collection orchestration
62
+
63
+ **Files:** Create ctx_cli/lib/ctx_cli/collection.rb; migrate VXLAN/Wireless cli/collect.rb.
64
+
65
+ **Interfaces:** Collection.new(adapter:, options:, progress:, batch:, label:, report_error:, failure_code:, out:, error:, input:).run(argv); adapter implements inventory, prepare, report, execute, collect and after_collect. options/progress are callable factories; batch implements the current Runner Batch#run/Completion protocol. No runner/connector require in ctx_cli.
66
+
67
+ - [x] Use existing consumer tests for fresh state, preflight, empty targets, failure isolation, interruption, thread cleanup and report failures as characterization gates.
68
+ - [x] Move shared run/finish/output flow once, keep adapters responsible for inventory, credentials, network/resources and domain snapshots.
69
+ - [x] Ensure factories remain injectable and both ordinary and PTY regression tests pass.
70
+
71
+ ### Task 5: Engineering and package integration
72
+
73
+ **Files:** ctx_cli/{README,AGENTS,CHANGELOG,Rakefile,.github/workflows,script/verify_package.rb,examples}; all touched consumer docs and package verifiers.
74
+
75
+ - [x] Add explicit CTX_CLI_PATH development override, candidate-package arguments and private installation checks; never auto-discover sibling code.
76
+ - [x] Add offline examples and smoke entrypoints, isolate loaded package paths and verify metadata/dependencies.
77
+ - [x] Mirror ctx_parser conventions with Ruby 3.4/4.0.6 matrix, explicit files and no remote release action.
78
+
79
+ ### Task 6: Verify and report
80
+
81
+ - [x] Run ctx_cli rake/build/package verification and available Ruby matrix locally.
82
+ - [x] Run VXLAN/Wireless complete rake and consumer package checks; net-connector script/ci; baseline rake with original missing-script failures explicitly recorded.
83
+ - [x] Self-review cross-gem dependency, resource ownership, output and initialization failures; fix concrete findings and rerun affected gates.
84
+ - [x] Record migration size, exact verification results, unchanged prior failures and remaining unverified boundaries.
85
+
86
+ ## Execution ledger
87
+
88
+ - Ruling: user continuation authorizes execution of the already described extraction; no extra approval cycle. Execute inline because the design-check subagent failed on workspace credits.
89
+ - Ruling: work in actual checkouts, preserving existing WIP, because current ctx_runner/Inventory changes are required inputs and checkout entrypoints must be verified. No reset/stash/branch switch/commit.
90
+ - Ruling: Command is a small parser object (#parse/#informational?) rather than a configurable rescue DSL; callers retain their current exit policy.
91
+ - Baseline before changes: VXLAN 128 CLI examples green, Wireless 108 green, connector 45 tests/476 assertions green; baseline coverage 33 metrics/8 platforms current; 4 existing backup replay spec failures from missing script.
92
+ - Preflight: Tasks 3–4 consume only explicitly injected components from Tasks 1–2. Runner Batch/Report and Inventory Filters remain existing collaborators, avoiding a connector → ctx_cli → connector cycle.
93
+
94
+ - Task 1: complete — generic configuration/initialization 7 examples green; connector original 45 tests/476 assertions green.
95
+ - Task 2: complete — command/terminal tests including standalone namespace regression: 14 examples green.
96
+ - Ruling: current Runner already exposes Batch#run/Completion; Collection delegates all finalization to that API and only maps terminal diagnostics/exit codes. Preserve the newer workspace implementation.
97
+
98
+ - Tasks 3–5: complete — all four consumers use the shared capabilities. baseline has no new command or runtime gem dependency. Candidate package arguments and explicit source overrides are documented.
99
+ - Task 6: complete for the authorized local scope — exact local evidence and existing failures are below. No commits, push, remote repository, CI run or release performed.
100
+
101
+ ## Final local verification (2026-10-22)
102
+
103
+ Environment: macOS arm64, Ruby 4.0.6, Bundler 4.0.20. Ruby 3.4 is configured in CI but unavailable locally; the remote matrix has not run. All network-device evidence is synthetic/offline or local PTY.
104
+
105
+ | Check | Result |
106
+ | --- | --- |
107
+ | ctx_cli `bundle exec rake` | 20 examples, 0 failures; 25 Ruby files lint-clean; both offline examples pass |
108
+ | ctx_cli build / independent install | Candidate 0.1.0 built; plain Ruby and minimal Bundler app pass in independent GEM_HOME/GEM_PATH; root and each capability load without host libraries |
109
+ | ctx_cli workflow lint | actionlint passes for CI/release workflows; no workflow executed remotely |
110
+ | VXLAN `bundle exec rake` | 455 examples, 0 failures; lint and examples pass |
111
+ | Wireless `bundle exec rake` | Latest full run: 317 examples, 0 failures; lint and examples pass (earlier workspace snapshot had 318; unrelated tests changed concurrently) |
112
+ | net-connector `bash script/ci` | 1232 tests, 12503 assertions, 0 failures/errors/skips; source/history/package scan, lint/actionlint, coverage comparison, benchmark smoke, build and installed plain/Bundler PTY smoke pass |
113
+ | Final connector configuration regression | 10 tests, 180 assertions, 0 failures/errors/skips after selector snapshot hardening |
114
+ | baseline full rake | 1330 examples, 4 failures, all in pre-existing backup_verification_spec.rb because script/verify_backups.rb is absent |
115
+ | baseline remaining gates | 241 Ruby files lint-clean; all existing examples complete; coverage current at 33 metrics / 8 platforms |
116
+ | baseline changed script | Coverage CLI regression passes; ordinary `ruby script/coverage.rb --check` and Bundler invocation pass; redundant load-path-dependent require removed |
117
+ | Consumer package checks | VXLAN/Wireless install explicit ctx_cli, ctx_runner, connector and expect candidate gems; connector installs exact ctx_cli candidate; candidate CLI files compared byte-for-byte; all pass |
118
+ | Actual checkout entrypoints | Without CTX_CLI_PATH/RUBYLIB: VXLAN/Wireless help, apply help, unknown command status, JSON inventory and stdin parse pass; backup help/init/show-config pass from its actual examples directory |
119
+
120
+ Configuration, initialization and collection checks cover precedence, false/nil, source paths, YAML/.env rejection, IO ownership, preflight before reports, preview without credentials, cancellation and post-publication faults. Existing consumer suites supply real Runner/connector integration coverage. New tests cover standalone namespace loading, opaque/cyclic data rejection and caller mutation of environment selector/path keys. The selector mutation was reproduced red before the copy/freeze fix. Parallel defaults/help remain host policy through `parallel_help:`.
121
+
122
+ The original backup checkout-root `.env` still contains obsolete `NC_CONCURRENCY`; invoking backup show-config from that root returns the existing validation error. The private file was not modified. Running the actual example entrypoint from its examples directory and isolated installed-package checks passes. This is a local configuration limitation, not a relaxed validator.
123
+
124
+ All previous work was preserved. Additional unrelated domain/report/test changes appeared during execution and were left intact. The initial receipt is `/var/folders/1w/b2h7_3sn6733tshd4w6_vvnc0000gn/T/ctx-cli-before-prquajq5`; the latest package checks use explicit artifacts, not adjacent source discovery. Local ignored lockfiles were resolved back to installed gems after source integration checks.
125
+
126
+ ## Extraction size
127
+
128
+ Compared against the pre-extraction receipt, counting only the migrated consumer source files (not all existing repository diffs):
129
+
130
+ | Consumer | Before | After | Removed |
131
+ | --- | ---: | ---: | ---: |
132
+ | VXLAN cli.rb + collect/options/parse/apply | 746 | 652 | 94 |
133
+ | Wireless same entrypoints | 788 | 695 | 93 |
134
+ | Connector Config + initializer + options/setup | 968 | 574 | 394 |
135
+ | baseline existing coverage script | 50 | 64 | -14 |
136
+ | Total | 2552 | 1985 | 567 |
137
+
138
+ This measures reduced consumer boilerplate, not total repository size. The new shared gem, tests, documentation and engineering scripts are additional code. Device protocol/command parsers, inventory selection and Runner state machines were not copied into ctx_cli.
@@ -0,0 +1,123 @@
1
+ # ctx_cli 公共命令行能力抽取设计
2
+
3
+ 日期:2026-10-22。状态:本地实现与验证完成;实际接口见 ctx_cli/README.md,验证结果及原有缺口见实施计划。
4
+
5
+ ## 目标与已确认范围
6
+
7
+ 用户希望把 vxlan/lib/vxlan/cli 一类宿主入口中的公共能力抽到独立 ctx_cli,减少 baseline、vxlan、backup、wireless 的样板代码,代码范式与工程规范参照 ctx_parser。
8
+
9
+ 用户已明确:baseline 只迁移现有脚本的公共逻辑,暂不增加 baseline 命令。此次不新增设备能力,不创建远程仓库,不提交、推送或发布。
10
+
11
+ 成功标准是公共行为有一个实现,消费模块保留必要的领域声明和适配;既有命令、参数、配置优先级、报告语义及错误退出行为保持可验证。不能只建一个无人使用的工具库,也不能以统一命名为理由改厂商解析器。
12
+
13
+ ## 当前依据
14
+
15
+ | 位置 | 已有职责及共性 |
16
+ | --- | --- |
17
+ | vxlan/lib/vxlan/cli.rb、wireless/lib/wireless/cli.rb | 懒加载分发、帮助、版本、参数错误、注入标准流 |
18
+ | 两个模块的 cli/options.rb | 文件发现、公开环境映射、默认值合并、公共参数、初始化、终端检查、领域校验 |
19
+ | 两个模块的 cli/parse.rb | 参数解析、帮助/示例、文件或 stdin、元数据/JSON 输出、完整性退出码 |
20
+ | 两个模块的 cli/collect.rb | 初始化和预览分支、全批预检、进度、调度、Runner 批次交付、失败和取消收尾 |
21
+ | 两个模块的 cli/apply.rb | 终端要求、精确确认及输出相同;计划格式、选择、校验、命令与会话租约不同 |
22
+ | net-connector-trunk/lib/net/connector/config.rb | 四类入口共用的配置发现、YAML/.env 安全读取、路径来源、配置快照 |
23
+ | net-connector-trunk/lib/net/connector/netdisco/cli/{options,setup}.rb | backup/TFTP/topology 公共选项、终端交互、初始化策略 |
24
+ | net-connector-trunk/lib/net/connector/netdisco/config/initializer.rb | 排他创建、私有权限、文件和目录同步,以及备份专有问答 |
25
+ | baseline/script/coverage.rb | 已有参数分支、输出和退出边界;矩阵生成属于 baseline |
26
+ | baseline/examples/*.rb | 大多直接演示领域 API,基本没有 CLI 框架代码,不能强行包装 |
27
+
28
+ 设计开始时 ctx_runner 已有实现,承接 Collection/Query/Observation/Batch/Report/Execution;本次在它的现有契约上接入。net-connector-trunk 另有清单筛选未提交改动,Inventory::Selection/Filters 继续是唯一筛选实现。所有这些工作均保留,不回滚、不整体覆盖。
29
+
30
+ baseline/spec/backup_verification_spec.rb 是已有未跟踪测试,但对应 script/verify_backups.rb 当前不存在。它是实施前已存在的独立缺口,不以本次抽取名义补建备份回放产品,也不能把该仓库全套测试称为通过。
31
+
32
+ 实施前已在各实际 checkout、Ruby 4.0.6、当前锁定/安装依赖下验证:VXLAN 的 cli_spec.rb + spec/cli 为 128 examples / 0 failures,Wireless 同范围为 108 / 0;net-connector 的 cli_backup、cli_setup、config_initializer、cli_config 为 45 tests / 476 assertions / 0 failures;baseline coverage --check 为 33 metrics / 8 platforms 且内容最新。baseline 的备份回放 spec 为 4 failures,根因仍是上述脚本缺失。这些是修改前基线,不是 ctx_cli 实现后的验证结果。
33
+
34
+ ## 方案选择
35
+
36
+ 采用独立小型 gem 加明确对象组合:CtxCli 命名空间、显式 require、构造参数注入、普通 OptionParser 扩展块;公共流程只有一个实现,领域策略作为协作者传入。
37
+
38
+ 不采用复制模板作为主要复用方式:它会继续复制错误修复。不采用一个包含所有业务模式的万能 CLI 基类或工作流 DSL:它会让 backup 的成功策略、无线 AP 租约和 VXLAN Fabric 规则变成大量隐式开关。
39
+
40
+ 工程以 ctx_parser 为参照:目录映射命名空间、中文代码注释、英文 RSpec/Changelog、RuboCop Omakase、显式打包清单、离线 examples、独立安装验证、固定 action 提交及发布前验证。明确差异是 ctx_cli 核心最低 Ruby 3.4,开发 Ruby 4.0.6,CI 覆盖两者;这是为了保持 net-connector 已有 Ruby 3.4 契约,而不是提高其最低版本。ctx_cli 不依赖 ctx_parser/ctx_runner,避免把它们的 Ruby 4.0.6 门槛传给 backup。
41
+
42
+ ## 依赖和职责边界
43
+
44
+ - ctx_cli 只依赖自己直接使用的标准库 gem 和 dotenv。require 不读取配置、不修改 ENV、不消费 stdin、不建目录、不加载设备驱动或 Rails。
45
+ - net-connector 依赖 ctx_cli 的宿主配置和终端能力;ctx_cli 不反向依赖 net-connector,避免循环。
46
+ - VXLAN/Wireless 显式依赖 ctx_cli;领域核心 require 不加载 CLI,帮助与离线解析不加载网络传输。
47
+ - ctx_runner 保留采集、报告检查点、完成判定与执行记账。ctx_cli 仅编排终端入口;Runner/进度/调度器通过协作者注入。
48
+ - Inventory::Selection/Filters 保留在 net-connector;不在 ctx_cli 再实现地址、厂商、名称或型号选择。
49
+ - baseline 只在现有脚本的宿主入口使用 ctx_cli,核心 Baseline API 不依赖 CLI 行为。若只用于开发脚本,依赖放 Gemfile;不凭空增加核心运行时依赖。
50
+
51
+ ## 公共能力和契约
52
+
53
+ ### Application 与 Command
54
+
55
+ Application 负责命令分发、全局帮助和版本,命令通过惰性 callable 加载。返回退出码,不调用 exit;标准流与 argv 归调用方所有。入口只声明名称、版本、帮助正文和各子命令工厂。
56
+
57
+ Command 负责每次解析的新参数快照、OptionParser 调用及帮助/示例短路。构造块接收 parser 和 values,入口在其中追加领域参数;#parse 返回值容器,#informational? 处理信息分支。领域执行与错误退出码留在宿主,不通过继承搜索隐式 hook。
58
+
59
+ 公共参数分组采用普通方法向 OptionParser 注册配置路径、并发、初始化、交互、JSON/进度、帮助等选项。字段名和选项说明可以显式注入。厂商、Fabric、AP、TFTP 和成功策略仍由宿主登记。列表按明确逗号分隔,拒绝空项,不依赖 Ruby 全局分隔符。
60
+
61
+ ### Configuration
62
+
63
+ 从当前 Net::Connector::Config 迁入通用文件加载实现。名称、配置环境键、共享文件名/目录、允许的共享命名空间与字段、路径字段及特殊字面量均由宿主显式给定;公共库不硬编码 vxlan、wireless、backup、topology 的业务字段。
64
+
65
+ 保持 CLI > 进程环境 > YAML > .env > 默认值的有效优先级。文件读取与工具字段转换分开;领域 Options 保留 schema、环境编码和最终校验。数组整层替换,false/nil 与缺失分开;被覆盖的低优先级坏值不提前误报。YAML/.env 相对路径按各自来源解析,命令行路径按启动目录解析。显式文件缺失不能回退。保留来源路径与字段归属,不在 sources 中展示秘密。
66
+
67
+ 保持批次快照、不修改 ENV、不冻结调用方输入、拒绝 YAML 别名/重复键/多文档/.env 命令替换等现有约束。Net::Connector::Config 保留原入口和配置政策作为薄适配,不保留第二份文件加载实现。既有 net-connector.yml、环境变量和默认搜索位置不改名。
68
+
69
+ ### Initialization 与 Terminal
70
+
71
+ Initialization 只负责发布调用方已经校验的公开配置或模板:目录 0700、文件 0600,先写完并同步临时文件,再排他发布并同步父目录;不覆盖已有文件。普通已有文件、符号链接、目录、竞争创建、取消及发布后同步失败分别处理。发布后的持久性未确认不能返回成功;清理故障不能掩盖原始故障。
72
+
73
+ 备份的逐字段问答和策略验证留在 net-connector 的 Initializer,底层文件发布委托给公共实现。VXLAN/Wireless 的初始化也使用同一实现;仍然在初始化后退出,不读取远端清单或连接设备,不把当前凭据保存进模板。
74
+
75
+ Terminal 封装真实终端检查、逐行读取、普通问答、隐藏密码和精确 token 确认;不拥有或关闭注入的 IO。默认自动化不读 stdin;backup 的 stdin-credentials 协议继续独占输入。apply 必须检查原有要求的全部终端流,并比较宿主提供的完整 token,不添加 --yes、不截断 token。
76
+
77
+ ### Input 与 Output
78
+
79
+ Input 统一显式文件或调用方 stdin 的读取,仅关闭自行打开的文件。Output 提供显式 JSON 与文本输出及安全诊断;不推断任意对象的敏感性,不把异常 message/cause 自动串入输出,不把业务结果成功与输出成功混为一谈。
80
+
81
+ VXLAN/Wireless Parse 使用 Command/Input/Output;维度校验、配置/运行解析分支、平台与 AP 参数、健康说明仍在领域模块。baseline 的 coverage 脚本复用参数与输出边界,生成内容、--check、原退出码及默认写入目标保持一致;不把领域示例里的单句 puts 全部替换为无价值包装。
82
+
83
+ ### Collection 入口编排
84
+
85
+ 公共 Collection 承接 VXLAN/Wireless 已重复的入口顺序与失败收尾,但不实现 Runner 或连接器。它显式接收 adapter、options/progress 工厂、batch 类和标准流。Batch#run/Completion 负责业务收尾;CLI 只映射诊断和退出码。每次 run 创建独立状态,失败不能改写上次批次。
86
+
87
+ adapter 的协议为:
88
+
89
+ - inventory(options, progress:):返回 targets 和筛选前 total;不读取设备凭据。
90
+ - prepare(targets, options):预检全批凭据、端点与资源,返回仅本批使用的上下文;失败发生在报告创建和设备连接之前。
91
+ - report(options):创建领域报告;公共流程据此构造注入的 Runner Batch。
92
+ - execute(targets, options) { |target| ... }:使用注入调度器或现有连接器 Pool;返回前必须回收工作线程。
93
+ - collect(target, options, context:, progress:):返回领域 snapshot;每设备会话及 AP/Fabric 等策略仍属于领域。
94
+ - after_collect(report, options):明确的收尾接口;VXLAN 在这里执行现有 Fabric 汇总,Wireless 使用空实现。
95
+
96
+ options 工厂构造当前领域 Options;Options 暴露现有 informational?/validate_interaction!/load_environment/initialize_files/missing?/load/config/flags/values/sources/public_values 协议。
97
+
98
+ 公共流程是:解析和信息短路 → 文件初始化/加载 → 公开配置或清单预览 → 空清单结束 → 全批预检 → 创建报告 → 调度采集并交付 Batch → 领域汇总 → Batch 收尾 → 输出。错误分类、取消优先级、已完成证据保留和摘要恢复行为以现有消费测试为准。无法保证已回收工作线程的调度器不能使用该协议。
99
+
100
+ backup/TFTP/topology 不强行接入此采集流程:其 Planner、BackupRun、成功策略、TFTP 回执和归档继续由 net-connector 负责;它们复用配置、选项、交互和初始化能力。apply 只复用终端/参数/展示组件,计划模式、哈希文件、单项选择、ChangePlan 验证与操作租约不移动。
101
+
102
+ ## 实施顺序
103
+
104
+ 1. 保存各仓库初始差异清单,跑现有定向契约,记录既有失败;建立 ctx_cli 的工程、公开边界测试和离线例子。
105
+ 2. 迁移通用配置读取与安全初始化,保留 net-connector 的旧入口适配;先运行其配置、初始化及 backup CLI 测试。
106
+ 3. 抽取参数、终端、输入输出和分发;接入 VXLAN/Wireless parse/options/apply,以及 backup/topology 的相应公共片段。
107
+ 4. 抽取 VXLAN/Wireless Collection 的入口流程,保留领域适配,并使用既有失败、取消、并发和 PTY 回归验证。
108
+ 5. baseline 只调整 coverage 脚本宿主边界;检查生成的 metric-coverage.md 字节不变及原退出行为。
109
+ 6. 更新 README、AGENTS、Unreleased、Gemfile/gemspec、候选包校验与联调入口;实际 checkout 和隔离安装分别验证。
110
+
111
+ 源码联调使用显式 CTX_CLI_PATH(沿用现有 CTX_RUNNER_PATH 约定),不自动把相邻源码加入加载路径。不改生产代码偷偷探测 ../ctx_cli。未发布版本先构建并安装候选包;隔离安装脚本显式接受候选 ctx_cli/ctx_runner 包,不假设公共索引已有它们。
112
+
113
+ ## 验收与完成边界
114
+
115
+ - ctx_cli:RSpec、RuboCop、离线 examples、构建、隔离安装、Ruby 3.4/4.0.6 的可用本地检查;远程 CI 未实际运行时只交付配置,不称 CI 通过。
116
+ - 配置:各层覆盖、静态/动态清单优先级、来源路径、空 selector、未知字段、凭据隔离、可变输入和污染的 Ruby 全局分隔符。
117
+ - 初始化/终端:帮助零副作用、非 TTY 不消费输入、现有文件不覆盖、竞争/符号链接、私有权限、目录同步失败、精确确认与取消。
118
+ - 采集:预检先于副作用、预览不需要凭据、无目标非成功、单设备失败隔离、重复/零次会话回调、线程回收、报告/输出失败、取消优先级和重复运行状态隔离。
119
+ - 消费者:VXLAN/Wireless 完整 rake;net-connector script/ci;baseline 现有测试与 coverage --check,已有缺失回放脚本的失败单独报告;不得跳过后把全套称为通过。
120
+ - 打包:直接从当前 checkout 运行现有 bin/examples;独立 GEM_HOME/GEM_PATH 安装候选包,清理 Bundler/RUBYLIB 影响,确认加载来源与文件清单。
121
+ - 完成说明提供抽出能力、各消费者实际接入点、样板代码减少的前后差异、执行的验证和未验证边界。不声称真实设备效果,不发布版本。
122
+
123
+ 接口内部的小型对象拆分、测试文件布局、必要的错误类型和方法命名由实施者根据现有测试决定;不得借此扩大业务功能、变更 CLI 契约、引入循环依赖或放宽失败判定。
@@ -12,14 +12,14 @@ module Vxlan
12
12
  def validate_parameters!
13
13
  super
14
14
  name, direction = @change.values_at("acl", "direction")
15
- raise ArgumentError, "ACL 参数无效" unless name.is_a?(String) && name.match?(/\A[A-Za-z0-9_.-]{1,63}\z/) && %w[in out].include?(direction)
16
- raise ArgumentError, "ACL 创建状态无效" unless [ true, false ].include?(@change["create_acl"])
17
- raise ArgumentError, "不能覆盖既有绑定" unless [ nil, name ].include?(@change["previous_binding"])
15
+ raise ArgumentError, "Invalid ACL parameters" unless name.is_a?(String) && name.match?(/\A[A-Za-z0-9_.-]{1,63}\z/) && %w[in out].include?(direction)
16
+ raise ArgumentError, "Invalid ACL creation state" unless [ true, false ].include?(@change["create_acl"])
17
+ raise ArgumentError, "Cannot overwrite an existing binding" unless [ nil, name ].include?(@change["previous_binding"])
18
18
  groups = @change.fetch("address_groups", {})
19
- raise ArgumentError, "ACL 依赖未验证" unless groups.is_a?(Hash)
19
+ raise ArgumentError, "ACL dependencies are unverified" unless groups.is_a?(Hash)
20
20
  @policy = AclPolicy.resolve({ "address_groups" => groups }, @change["entries"])
21
- raise ArgumentError, "ACL 规则或依赖未验证" unless @policy && @policy.address_groups == groups
22
- raise ArgumentError, "无有效 ACL 规则" if @policy.rules.empty?
21
+ raise ArgumentError, "ACL rules or dependencies are unverified" unless @policy && @policy.address_groups == groups
22
+ raise ArgumentError, "No valid ACL rules" if @policy.rules.empty?
23
23
  @rule_commands = rule_commands
24
24
  end
25
25
 
@@ -29,7 +29,7 @@ module Vxlan
29
29
  next if entry["action"] == "remark"
30
30
  action, expression = entry.values_at("action", "expression")
31
31
  unless %w[permit deny].include?(action) && expression.is_a?(String) && expression.match?(/\A(?:ip|tcp|udp|icmp|igmp|gre|esp|ah|ospf) [A-Za-z0-9_.:\/ -]+\z/)
32
- raise ArgumentError, "ACL 规则语法未验证"
32
+ raise ArgumentError, "ACL rule syntax is unverified"
33
33
  end
34
34
  " #{entry['sequence']} #{action} #{expression}"
35
35
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "ctx_runner/fingerprint"
4
- require "json"
5
4
  require "ipaddr"
6
5
  require "ctx_parser/immutable"
7
6
  require_relative "acl_policy"
@@ -16,12 +15,12 @@ module Vxlan
16
15
 
17
16
  # 类型判别只发生在输入边界,不接受任意脚本或未声明字段。
18
17
  def self.build(change)
19
- raise ArgumentError, "增量须为映射" unless change.is_a?(Hash)
18
+ raise ArgumentError, "Change must be a mapping" unless change.is_a?(Hash)
20
19
  case change["kind"]
21
20
  when "vlan_vni_add" then VlanVni.new(change)
22
21
  when "svi_gateway_add" then SviGateway.new(change)
23
22
  when "svi_acl" then SviAcl.new(change)
24
- else raise ArgumentError, "不支持的增量类型"
23
+ else raise ArgumentError, "Unsupported change type"
25
24
  end
26
25
  end
27
26
 
@@ -33,17 +32,11 @@ module Vxlan
33
32
  nil
34
33
  end
35
34
 
36
- # 字符串键规范化后计算语义指纹;Hash 顺序不影响结果,数组保留业务顺序。
37
- def self.fingerprint(value) = CtxRunner::Fingerprint.call(value)
38
-
39
- # 兼容既有领域入口,配置身份协议仅由 Runner 实现。
40
- def self.canonical(value) = CtxRunner::Fingerprint.canonical(value)
41
-
42
35
  # 构造时一次校验和渲染,随后调用者不能改变已确认的参数或命令。
43
36
  def initialize(change)
44
- raise ArgumentError, "请使用 ChangePlan.build 构造业务增量" if instance_of?(ChangePlan)
37
+ raise ArgumentError, "Use ChangePlan.build to construct a domain change" if instance_of?(ChangePlan)
45
38
  unless change.is_a?(Hash) && change.keys.all?(String) && (change.keys - self.class::FIELDS).empty? && change["kind"] == self.class::KIND
46
- raise ArgumentError, "增量字段无效"
39
+ raise ArgumentError, "Invalid change fields"
47
40
  end
48
41
  @change = CtxParser::Immutable.copy(change)
49
42
  validate_parameters!
@@ -73,8 +66,8 @@ module Vxlan
73
66
 
74
67
  # 三种动作均定位已有 L2 业务,VLAN/VNI 不从字符串或名称猜测。
75
68
  def validate_parameters!
76
- raise ArgumentError, "VLAN 无效" unless @change["vlan"].is_a?(Integer) && @change["vlan"].between?(1, 4094)
77
- raise ArgumentError, "VNI 无效" unless @change["vni"].is_a?(Integer) && @change["vni"].between?(1, 16_777_215)
69
+ raise ArgumentError, "Invalid VLAN" unless @change["vlan"].is_a?(Integer) && @change["vlan"].between?(1, 4094)
70
+ raise ArgumentError, "Invalid VNI" unless @change["vni"].is_a?(Integer) && @change["vni"].between?(1, 16_777_215)
78
71
  end
79
72
 
80
73
  # 网关相关增量必须携带明确租户及单个已验证 IPv4 前缀。
@@ -82,7 +75,7 @@ module Vxlan
82
75
  address = @change["address"]
83
76
  unless @change["vrf"].is_a?(String) && !@change["vrf"].empty? && address.is_a?(String) &&
84
77
  address.match?(/\A\d{1,3}(?:\.\d{1,3}){3}\/\d{1,2}\z/) && IPAddr.new(address).ipv4?
85
- raise ArgumentError, "缺少已验证的业务网关"
78
+ raise ArgumentError, "Verified service gateway is missing"
86
79
  end
87
80
  end
88
81
 
@@ -112,7 +105,7 @@ module Vxlan
112
105
 
113
106
  # 元数据与可推导事实不参与漂移比较;每种动作只移除其允许变化的字段。
114
107
  def preserved_context(config)
115
- context = ChangePlan.canonical(config).except("config_digest", "review_status", "review_findings", "segments", "unnumbered_addresses")
108
+ context = CtxRunner::Fingerprint.canonical(config).except("config_digest", "review_status", "review_findings", "segments", "unnumbered_addresses")
116
109
  context.fetch("acls", {}).transform_values! { |entries| AclPolicy.rules(entries) }
117
110
  remove_changed_fields(context)
118
111
  context
@@ -70,13 +70,13 @@ module Vxlan
70
70
  scope.merge!(router_id: match[1], local_as: match[2])
71
71
  elsif (match = /\ANeighbor\s+T\s+AS\s+(?:(PfxRcd)\s+)?(Type-[1-9]\d*(?:\s+Type-[1-9]\d*)*)\z/.match(line))
72
72
  prefix_total = !match[1].nil?
73
- prefix_columns = match[2].split.map { |column| column.downcase.delete("-").to_sym }
73
+ prefix_columns = match[2].split(" ").map { |column| column.downcase.delete("-").to_sym }
74
74
  if prefix_columns.uniq != prefix_columns || (prefix_columns - PREFIX_TYPES).any?
75
75
  unparsed_records(number)
76
76
  prefix_columns = nil
77
77
  end
78
78
  elsif prefix_columns
79
- words = line.split
79
+ words = line.split(" ")
80
80
  address = CtxParser::Values.ip_address(words[0], allow_zone: true) if ip_address?(words[0])
81
81
  peer = peers[[ scope[:vrf], scope[:address_family], address ]]
82
82
  offset = prefix_total ? 4 : 3
@@ -23,7 +23,7 @@ module Vxlan
23
23
  elsif line.match?(/\AInterface(?:\s+Ch #)?\z/i)
24
24
  next
25
25
  else
26
- words = line.split
26
+ words = line.split(" ")
27
27
  state = words.index { |word| %w[up down connected notconnect disabled admin-down].include?(word.downcase) }
28
28
  unless layout && state && INTERFACE.match?(words[0])
29
29
  unparsed_records(number)
@@ -51,7 +51,7 @@ module Vxlan
51
51
  # 将回显的聚合成员归一化为接口身份及状态标志。
52
52
  def parse_members(text)
53
53
  return [] if text.empty? || text == "--"
54
- text.split.filter_map do |word|
54
+ text.split(" ").filter_map do |word|
55
55
  match = /\A(Eth(?:ernet)?[\d\/]+)\(([^\s()]+)\)\z/i.match(word)
56
56
  @issues << { code: :invalid_port_channel_member } unless match
57
57
  { interface: match[1], flags: match[2] } if match
@@ -239,7 +239,7 @@ module Vxlan
239
239
 
240
240
  # 提取支持的键值配置对。
241
241
  def parse_keyword_pairs(text, allowed)
242
- words = text.split
242
+ words = text.split(" ")
243
243
  raise ArgumentError unless words.size.even? && (words.each_slice(2).map(&:first) - allowed).empty?
244
244
  values = words.each_slice(2).to_h.transform_keys(&:to_sym)
245
245
  raise ArgumentError unless values.size == words.size / 2
@@ -250,7 +250,7 @@ module Vxlan
250
250
 
251
251
  # 归一化静态路由及可选下一跳属性。
252
252
  def static_route(block)
253
- words = block.text.split
253
+ words = block.text.split(" ")
254
254
  raise ArgumentError if words.size < 4
255
255
  validate_static_next_hop(words.drop(3))
256
256
 
@@ -76,7 +76,7 @@ module Vxlan
76
76
  analyze_children(block) do |child|
77
77
  case child.text
78
78
  when /\Amatch (ip|ipv6) (address|next-hop|route-source) prefix-list (.+)\z/
79
- family, selector, names = $1 == "ip" ? "ipv4" : "ipv6", $2, $3.split
79
+ family, selector, names = $1 == "ip" ? "ipv4" : "ipv6", $2, $3.split(" ")
80
80
  values[:prefix_lists].concat(names) if family == "ipv4" && selector == "address"
81
81
  (values[:prefix_list_matches] ||= []) << { address_family: family, selector: selector, names: names }
82
82
  when /\Amatch / then values[:additional_matches] << child.text
@@ -54,7 +54,7 @@ module Vxlan
54
54
  next issue(child)
55
55
  end
56
56
 
57
- (values[:networks] ||= []) << { prefix: network_prefix(subnet, family: family.split.first), route_map: route_map }.compact
57
+ (values[:networks] ||= []) << { prefix: network_prefix(subnet, family: family.split(" ").first), route_map: route_map }.compact
58
58
  when /\Aredistribute (\S+)(?: (?!route-map\b)(\S+))?(?: route-map (\S+))?\z/
59
59
  values[:redistribute] << { protocol: $1, process: $2, route_map: $3 }.compact
60
60
  when /\Aretain route-target (all|route-map \S+)\z/ then assign(values, :retain_route_target, $1, child)