moose-inventory 1.0.8 → 2.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.
Files changed (104) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +49 -0
  3. data/.github/workflows/release.yml +58 -0
  4. data/.gitignore +1 -1
  5. data/.gitleaks.toml +9 -0
  6. data/.rubocop.yml +19 -784
  7. data/BACKLOG.md +290 -0
  8. data/Gemfile.lock +95 -0
  9. data/README.md +38 -9
  10. data/Rakefile +1 -1
  11. data/bin/moose-inventory +1 -1
  12. data/docs/release/publishing.md +109 -0
  13. data/docs/release/release-readiness.md +55 -0
  14. data/docs/security-audit-2026-05-21.md +71 -0
  15. data/docs/security-audit-2026-05-26-rerun.md +75 -0
  16. data/docs/security-audit-2026-05-26.md +63 -0
  17. data/lib/moose_inventory/cli/formatter.rb +16 -17
  18. data/lib/moose_inventory/cli/group.rb +4 -1
  19. data/lib/moose_inventory/cli/group_add.rb +89 -75
  20. data/lib/moose_inventory/cli/group_addchild.rb +84 -71
  21. data/lib/moose_inventory/cli/group_addhost.rb +78 -69
  22. data/lib/moose_inventory/cli/group_addvar.rb +37 -37
  23. data/lib/moose_inventory/cli/group_get.rb +23 -26
  24. data/lib/moose_inventory/cli/group_list.rb +12 -15
  25. data/lib/moose_inventory/cli/group_listvars.rb +12 -14
  26. data/lib/moose_inventory/cli/group_rm.rb +104 -76
  27. data/lib/moose_inventory/cli/group_rmchild.rb +99 -54
  28. data/lib/moose_inventory/cli/group_rmhost.rb +64 -60
  29. data/lib/moose_inventory/cli/group_rmvar.rb +5 -5
  30. data/lib/moose_inventory/cli/helpers.rb +76 -0
  31. data/lib/moose_inventory/cli/host.rb +4 -1
  32. data/lib/moose_inventory/cli/host_add.rb +51 -66
  33. data/lib/moose_inventory/cli/host_addgroup.rb +77 -68
  34. data/lib/moose_inventory/cli/host_addvar.rb +6 -6
  35. data/lib/moose_inventory/cli/host_get.rb +15 -18
  36. data/lib/moose_inventory/cli/host_list.rb +3 -3
  37. data/lib/moose_inventory/cli/host_listvars.rb +21 -23
  38. data/lib/moose_inventory/cli/host_rm.rb +9 -9
  39. data/lib/moose_inventory/cli/host_rmgroup.rb +63 -60
  40. data/lib/moose_inventory/cli/host_rmvar.rb +3 -3
  41. data/lib/moose_inventory/config/config.rb +43 -40
  42. data/lib/moose_inventory/db/db.rb +92 -52
  43. data/lib/moose_inventory/db/models.rb +11 -12
  44. data/lib/moose_inventory/inventory_context.rb +50 -0
  45. data/lib/moose_inventory/operations/add_associations.rb +127 -0
  46. data/lib/moose_inventory/operations/add_groups.rb +115 -0
  47. data/lib/moose_inventory/operations/add_hosts.rb +110 -0
  48. data/lib/moose_inventory/operations/group_child_relations.rb +118 -0
  49. data/lib/moose_inventory/operations/group_cleanup.rb +55 -0
  50. data/lib/moose_inventory/operations/remove_associations.rb +101 -0
  51. data/lib/moose_inventory/operations/remove_groups.rb +79 -0
  52. data/lib/moose_inventory/version.rb +1 -1
  53. data/moose-inventory.gemspec +38 -20
  54. data/scripts/check.sh +10 -0
  55. data/scripts/ci/check_permissions.sh +35 -0
  56. data/scripts/ci/check_rubocop.sh +28 -0
  57. data/scripts/ci/check_secrets.sh +26 -0
  58. data/scripts/ci/check_security.sh +68 -0
  59. data/scripts/ci/install_security_tools.sh +47 -0
  60. data/scripts/ci/package_sanity.sh +46 -0
  61. data/scripts/files.rb +1 -4
  62. data/scripts/install_dependencies.sh +19 -0
  63. data/scripts/reports.sh +2 -2
  64. data/spec/lib/moose_inventory/cli/cli_spec.rb +13 -14
  65. data/spec/lib/moose_inventory/cli/group_add_spec.rb +118 -119
  66. data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +49 -51
  67. data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +80 -83
  68. data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +91 -91
  69. data/spec/lib/moose_inventory/cli/group_get_spec.rb +22 -23
  70. data/spec/lib/moose_inventory/cli/group_list_spec.rb +19 -20
  71. data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +35 -36
  72. data/spec/lib/moose_inventory/cli/group_rm_spec.rb +115 -78
  73. data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +86 -45
  74. data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +43 -46
  75. data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +131 -131
  76. data/spec/lib/moose_inventory/cli/group_spec.rb +9 -9
  77. data/spec/lib/moose_inventory/cli/host_add_spec.rb +103 -43
  78. data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +78 -80
  79. data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +122 -122
  80. data/spec/lib/moose_inventory/cli/host_get_spec.rb +16 -16
  81. data/spec/lib/moose_inventory/cli/host_list_spec.rb +8 -8
  82. data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +50 -52
  83. data/spec/lib/moose_inventory/cli/host_rm_spec.rb +12 -12
  84. data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +48 -51
  85. data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +136 -136
  86. data/spec/lib/moose_inventory/config/config_spec.rb +16 -3
  87. data/spec/lib/moose_inventory/db/db_spec.rb +386 -2
  88. data/spec/lib/moose_inventory/db/models_spec.rb +10 -11
  89. data/spec/lib/moose_inventory/operations/add_associations_spec.rb +77 -0
  90. data/spec/lib/moose_inventory/operations/add_groups_spec.rb +65 -0
  91. data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +69 -0
  92. data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +76 -0
  93. data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +78 -0
  94. data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +57 -0
  95. data/spec/shared/shared_config_setup.rb +2 -2
  96. data/spec/spec_helper.rb +7 -8
  97. metadata +157 -105
  98. data/.coveralls.yml +0 -0
  99. data/Guardfile +0 -38
  100. data/config/dotfiles/coveralls.yml +0 -0
  101. data/config/dotfiles/gitignore +0 -20
  102. data/config/dotfiles/rubocop.yml +0 -793
  103. data/scripts/guard_quality.sh +0 -3
  104. data/scripts/guard_test.sh +0 -2
@@ -0,0 +1,71 @@
1
+ # Security audit — 2026-05-21
2
+
3
+ Scope: local static/security review of the `moose-inventory` Ruby CLI/gem at commit `ff522502d5981314c451e855be10cbbc7ebeba48`, plus the hardening changes on branch `security-audit-2026-05-21`.
4
+
5
+ ## Executive summary
6
+
7
+ The audit found one actionable dependency vulnerability and one low-risk repository hygiene issue. Both were remediated in this branch:
8
+
9
+ 1. Development dependency `rake 10.5.0` was affected by CVE-2020-8130 / GHSA-jppv-gw3r-w3q8, an OS command injection issue in `Rake::FileList` for filenames beginning with `|`. The gemspec now requires `rake >= 13.0, < 14`, and `Gemfile.lock` resolves `rake 13.4.2`.
10
+ 2. Most source, config, docs, and spec files were executable (`100755`) even though they are not entrypoints. The branch normalizes non-executable files to `100644`, keeping only `bin/moose-inventory` and actual scripts executable.
11
+
12
+ After the dependency update, an OSV query for locked RubyGems dependencies returned zero known vulnerabilities. Semgrep Ruby rules returned zero findings.
13
+
14
+ ## Surfaces reviewed
15
+
16
+ - CLI entrypoint: `bin/moose-inventory`
17
+ - Global config parsing and file loading: `lib/moose_inventory/config/config.rb`
18
+ - DB connection and schema creation: `lib/moose_inventory/db/db.rb`
19
+ - Sequel models and associations: `lib/moose_inventory/db/models.rb`
20
+ - CLI command handlers under `lib/moose_inventory/cli/`
21
+ - Packaging metadata: `moose-inventory.gemspec`, `Gemfile`, `Gemfile.lock`
22
+ - Helper scripts under `scripts/`
23
+ - Test fixtures/config under `spec/`
24
+
25
+ ## Findings fixed
26
+
27
+ ### P2 — Vulnerable development dependency: `rake 10.5.0`
28
+
29
+ - Evidence: `Gemfile.lock` resolved `rake (10.5.0)` and `moose-inventory.gemspec` constrained rake to `~> 10`.
30
+ - Advisory: OSV/GitHub Advisory `GHSA-jppv-gw3r-w3q8`, CVE-2020-8130.
31
+ - Impact: local OS command injection in vulnerable Rake versions when `Rake::FileList` receives a filename beginning with the pipe character (`|`). This is primarily a developer/build-time risk, not a runtime CLI inventory risk.
32
+ - Fix: changed the development dependency to `rake >= 13.0, < 14` and refreshed `Gemfile.lock` to `rake 13.4.2`.
33
+ - Validation: OSV query after the update returned `deps_with_vulns 0`.
34
+
35
+ ### P4 — Over-broad executable bits on repository files
36
+
37
+ - Evidence: file inventory showed `100755` executable mode on `Gemfile`, `README.md`, library files, specs, config, and other non-entrypoint files.
38
+ - Impact: low. This does not create a direct vulnerability by itself, but it expands accidental execution surface and makes packaging/review noisier than necessary. Tiny footgun, sharp enough to remove.
39
+ - Fix: normalized non-entrypoint files to `100644`; retained executable mode for `bin/moose-inventory` and scripts with shebangs.
40
+
41
+ ## Notable negative findings
42
+
43
+ - Config deserialization now uses `YAML.safe_load_file` with aliases disabled and no permitted classes/symbols.
44
+ - No shell execution sinks were found in runtime code. The backtick use in `moose-inventory.gemspec` is the normal `git ls-files` packaging pattern.
45
+ - Database access uses Sequel model/hash APIs for user-provided names and variables; no raw SQL interpolation was identified in the reviewed CLI/database paths.
46
+ - No committed secrets were identified outside expected example/test placeholder passwords.
47
+ - No external network-facing service, HTTP route, RPC handler, webhook, queue consumer, or upload parser exists in this repo; the meaningful attack surface is local CLI usage and build/development tooling.
48
+
49
+ ## Tooling evidence
50
+
51
+ - Inventory: 79 files; Ruby manifests: `Gemfile`, `Gemfile.lock`.
52
+ - Semgrep: `semgrep --config p/ruby --json --quiet .` returned 0 findings.
53
+ - OSV dependency query before fix: 1 vulnerable dependency (`rake 10.5.0`, `GHSA-jppv-gw3r-w3q8`).
54
+ - OSV dependency query after fix: 41 RubyGems dependency records queried, 0 dependencies with known vulnerabilities.
55
+ - `bundle-audit`, `osv-scanner`, `gitleaks`, `trufflehog`, and `brakeman` were not installed in this environment. Brakeman is also Rails-specific and not expected to apply here.
56
+
57
+ ## Residual risks / future hardening
58
+
59
+ - Consider adding a CI security job for OSV or bundler-audit so dependency advisories are caught before they fossilize in the lockfile like a tiny Jurassic Park exhibit.
60
+ - Consider keeping generated coverage artifacts out of normal grep/scanner paths; they are ignored from this audit because they contain large bundled HTML/CSS assets.
61
+
62
+ ## GitHub Dependabot follow-up after first push
63
+
64
+ After pushing the audit remediation, GitHub emitted a default-branch warning for 7 Dependabot vulnerabilities. Querying the repository Dependabot alerts through `gh api repos/RusDavies/moose-inventory/dependabot/alerts` showed that all 7 were already in `fixed` state after the modernization/security-audit commits reached GitHub:
65
+
66
+ - `rake`: GHSA-jppv-gw3r-w3q8 / CVE-2020-8130, fixed by `rake >= 13.0, < 14` and lockfile `rake 13.4.2`.
67
+ - `json`: GHSA-jphg-qwrw-7w9g / CVE-2020-10663, fixed by the existing current constraint `json >= 2.7, < 3` and lockfile `json 2.19.5`.
68
+ - `rubocop`: GHSA-wmjf-jpjj-9f3j / CVE-2017-8418, fixed by removing RuboCop as a development dependency.
69
+ - `bundler`: GHSA-jvgm-pfqv-887x / CVE-2016-7954, GHSA-g98m-96g9-wfjq / CVE-2019-3881, GHSA-fp4w-jxhp-m23p / CVE-2020-36327, and GHSA-fj7f-vq84-fh43 / CVE-2021-43809. GitHub marked these fixed because the lockfile uses Bundler 2.6.9; this follow-up also tightens the gemspec development dependency to `bundler >= 2.2.33, < 3` so fresh development installs cannot select known-vulnerable Bundler 1.x/early 2.x releases.
70
+
71
+ Follow-up validation: `gh api 'repos/RusDavies/moose-inventory/dependabot/alerts?state=open'` returned no open alerts.
@@ -0,0 +1,75 @@
1
+ # Security Audit Rerun — 2026-05-26
2
+
3
+ Repository: `RusDavies/moose-inventory`
4
+ Local path: `/home/skippy/.openclaw/workspace/projects/moose-inventory`
5
+ Audited commit at start: `a07b5c89214a3cee66170217c5b38e9ad2ae093a`
6
+ Audit branch: `security-audit-2026-05-26-rerun`
7
+ Evidence store: `.openclaw-security-audit/audit.sqlite`, audit run `2`
8
+
9
+ ## Executive summary
10
+
11
+ The rerun found no exploitable application vulnerabilities in the Ruby CLI/config/database code reviewed, and all deterministic dependency, advisory, package, and secret-scanning gates passed.
12
+
13
+ One release-supply-chain hardening gap was identified and fixed during the audit: the release workflow ran `./scripts/check.sh` without installing or requiring the dedicated security tools, so a tag-based release could publish even if `gitleaks`, `osv-scanner`, or `bundler-audit` coverage was absent from that release job. CI already enforced those tools; release now does too.
14
+
15
+ ## Scope
16
+
17
+ Reviewed security-relevant surfaces and changes since the prior audit:
18
+
19
+ - GitHub Actions CI and release workflows.
20
+ - Security-tool installation and enforcement scripts.
21
+ - Ruby CLI entrypoints and Thor command surfaces.
22
+ - YAML configuration loading.
23
+ - SQLite/MySQL/PostgreSQL connection setup and password handling.
24
+ - Recursive group deletion behavior touched by recent issue work.
25
+ - Gem packaging/release path.
26
+
27
+ ## Deterministic results
28
+
29
+ - Full local required-tool gate: passed.
30
+ - RSpec: 268 examples, 0 failures.
31
+ - Coverage: 96.52% line coverage.
32
+ - Custom OSV dependency check: 45 dependencies queried, 0 vulnerable.
33
+ - `bundler-audit`: no vulnerabilities found.
34
+ - `osv-scanner`: no issues found in `Gemfile.lock`.
35
+ - `gitleaks`: dedicated secret scan passed.
36
+ - Package sanity: built and inspected `tmp/pkg/moose-inventory.gem` successfully.
37
+ - Semgrep Ruby registry scan: 62 tracked Ruby files scanned with 44 Ruby rules, 0 findings.
38
+ - GitHub Dependabot open alerts: 0.
39
+ - GitHub code scanning alerts: unavailable / no analysis found (`404`).
40
+ - GitHub secret scanning alerts: unavailable because secret scanning is disabled for this repository.
41
+ - Workflow YAML parse check: `ci.yml` and `release.yml` parsed successfully with Ruby Psych.
42
+ - Current GitHub CI before audit branch: latest `master` CI run succeeded for Ruby 3.2, 3.3, and 3.4.
43
+
44
+ ## Finding fixed during audit
45
+
46
+ ### SEC-RERUN-2026-05-26-01 — Release workflow did not require dedicated security tools
47
+
48
+ - Priority before fix: P2 medium, release supply-chain hardening.
49
+ - Exposure: tag-triggered release workflow.
50
+ - Affected file: `.github/workflows/release.yml`.
51
+ - Evidence: CI installed `gitleaks`/`osv-scanner` and set `MOOSE_INVENTORY_REQUIRE_SECURITY_TOOLS=1`, but the release workflow only ran `./scripts/check.sh`. In local mode, `scripts/ci/check_security.sh` and `scripts/ci/check_secrets.sh` intentionally skip missing optional security tools unless `MOOSE_INVENTORY_REQUIRE_SECURITY_TOOLS=1` is set.
52
+ - Impact: a release tag created from an unexpected commit or during a tooling/path issue could publish without the same dedicated SCA/secret-scan enforcement as CI.
53
+ - Fix applied: release workflow now sets up Go with cache disabled, installs the pinned security CLIs via `scripts/ci/install_security_tools.sh`, runs native dependency installation with a 5-minute timeout, and runs `./scripts/check.sh` with `MOOSE_INVENTORY_REQUIRE_SECURITY_TOOLS=1`.
54
+ - Verification: full local required-tool gate passed after the workflow change.
55
+ - Residual risk: release workflow can only be fully proven on the next real release tag because already-published `v1.0.9` must not be retagged.
56
+
57
+ ## Reviewed areas with no actionable finding
58
+
59
+ - YAML config loading uses `YAML.safe_load_file` with aliases disabled and no permitted classes/symbols.
60
+ - SQLite database path handling creates parent directories with `FileUtils.mkdir_p`; this is local config-driven CLI behavior, not a remotely reachable path traversal surface.
61
+ - MySQL/PostgreSQL password handling supports `password_env`; plaintext `password` remains for compatibility but README guidance discourages committing it.
62
+ - CLI input reaches Sequel model operations rather than shell execution; no shell/eval sink was identified in application code.
63
+ - Recent recursive group deletion is explicit opt-in and keeps host fallback behavior covered by regression tests.
64
+ - GitHub Actions release job uses OIDC/trusted publishing and does not store a RubyGems API key in the workflow.
65
+
66
+ ## Limitations
67
+
68
+ - This was a local/source and CI/release workflow audit, not an active test against live external databases or RubyGems publishing.
69
+ - GitHub code scanning is not configured, so there were no CodeQL/code-scanning results to review.
70
+ - GitHub secret scanning is disabled for the repository; local `gitleaks` coverage was used instead.
71
+ - The release trusted-publishing path still needs verification on the next real version tag.
72
+
73
+ ## Conclusion
74
+
75
+ No open exploitable vulnerabilities remain from this rerun. The only identified security gap was release-pipeline parity with CI security tooling, and it was fixed in this audit branch.
@@ -0,0 +1,63 @@
1
+ # Security audit — 2026-05-26
2
+
3
+ Scope: local static/security review of the `moose-inventory` Ruby CLI/gem at commit `8c3eaada5d70ef599961b8ca8b78e12ea4ce83c9` on branch `security-audit-2026-05-26`.
4
+
5
+ ## Executive summary
6
+
7
+ No actionable security vulnerabilities were identified in this pass.
8
+
9
+ The meaningful attack surface remains local CLI execution, configuration-file loading, database access through Sequel, package/release automation, and developer tooling. There is no HTTP server, RPC endpoint, webhook handler, queue consumer, file upload parser, shell-command execution path, or plugin system in this repository.
10
+
11
+ The areas remediated in the prior 2026-05-21 audit remain in good shape: YAML config loading uses `YAML.safe_load_file`, DB credentials can be supplied through environment variables, OSV reports no known vulnerable locked RubyGems dependencies, CI/package sanity gates are present, and GitHub Dependabot has no open alerts.
12
+
13
+ ## Surfaces reviewed
14
+
15
+ - CLI entrypoint: `bin/moose-inventory`
16
+ - Global option parsing and config loading: `lib/moose_inventory/config/config.rb`
17
+ - DB connection/schema/transaction code: `lib/moose_inventory/db/db.rb`
18
+ - Sequel models and associations: `lib/moose_inventory/db/models.rb`
19
+ - CLI command handlers under `lib/moose_inventory/cli/`
20
+ - Formatter/output serialization: `lib/moose_inventory/cli/formatter.rb`
21
+ - Packaging and release metadata: `moose-inventory.gemspec`, `Gemfile`, `Gemfile.lock`, `.github/workflows/ci.yml`, `.github/workflows/release.yml`
22
+ - Helper scripts under `scripts/`
23
+ - Test config/spec fixtures under `spec/`
24
+
25
+ ## Findings
26
+
27
+ No P0/P1/P2/P3 actionable findings were identified.
28
+
29
+ ## Notable negative findings
30
+
31
+ - Config deserialization uses `YAML.safe_load_file` with aliases disabled and no permitted classes/symbols.
32
+ - No runtime shell execution sinks were identified.
33
+ - Database access uses Sequel model/dataset/hash APIs for user-controlled names, groups, hosts, and variables; no raw SQL interpolation was identified in reviewed runtime paths.
34
+ - MySQL/PostgreSQL passwords can be supplied via `password_env`, and README guidance prefers environment-backed passwords over plaintext config values.
35
+ - No committed secrets were identified outside expected example/test placeholders.
36
+ - GitHub Actions release publishing uses RubyGems trusted publishing/OIDC rather than a stored RubyGems API key.
37
+ - Dependency advisory gate queried OSV for 41 locked RubyGems dependency records and reported zero known vulnerabilities.
38
+ - GitHub Dependabot open-alert query returned zero open alerts.
39
+
40
+ ## Tooling evidence
41
+
42
+ - Audit evidence store initialized at `.openclaw-security-audit/audit.sqlite` with `audit_run_id=1`.
43
+ - Inventory: 187 files; Ruby manifests detected: `Gemfile`, `Gemfile.lock`, plus generated package-sanity manifests under `tmp/package-sanity`.
44
+ - Symbol extractor scanned 156 files but did not extract Ruby symbols/surfaces with the current lightweight extractor.
45
+ - Semgrep auto-config failed because metrics are disabled; reran explicit Ruby registry rules instead.
46
+ - Semgrep: `semgrep --config p/ruby --json --metrics=off --exclude .openclaw-security-audit --exclude spec/reports --exclude tmp .` scanned 62 tracked Ruby files with 44 rules and returned 0 findings.
47
+ - Dependency advisory gate: `./scripts/ci/check_security.sh` queried 41 RubyGems dependencies and returned 0 vulnerable dependencies.
48
+ - Full local gate: `./scripts/check.sh` passed with 268 examples, 0 failures, 96.52% line coverage, OSV 0 vulnerabilities, and package sanity passed.
49
+ - GitHub Dependabot: `gh api 'repos/RusDavies/moose-inventory/dependabot/alerts?state=open' --jq 'length'` returned `0`.
50
+ - GitHub code-scanning alerts could not be queried because no code-scanning analysis exists for this repository; GitHub returned `404 no analysis found`.
51
+
52
+ ## Tooling limitations
53
+
54
+ - `osv-scanner`, `bundler-audit`/`bundle-audit`, `gitleaks`, `trufflehog`, `brakeman`, `flog`, and `reek` were not installed in this environment.
55
+ - `bundle exec rubocop` could not run because RuboCop is not part of the bundle. This is not a security gate failure, but it limits style/static-quality coverage.
56
+ - Secret scanning was limited to tracked-file grep patterns because dedicated secret scanners were unavailable.
57
+ - The audit did not perform active exploitation against external systems or live database servers.
58
+
59
+ ## Residual risks / recommendations
60
+
61
+ - Consider adding a dedicated secret scanner such as `gitleaks` or `trufflehog` to local/CI security tooling if this project will accept outside contributions.
62
+ - Consider adding `bundler-audit` or `osv-scanner` as an optional developer tool if broader advisory coverage is desired beyond the existing custom OSV gate.
63
+ - Keep generated coverage and package-sanity artifacts excluded from security scans; they are noisy and not part of the runtime gem surface.
@@ -15,37 +15,37 @@ module Moose
15
15
  def self.dump(arg, format = nil)
16
16
  out(arg, format)
17
17
  end
18
-
18
+
19
19
  def self.out(arg, format = nil)
20
20
  return if arg.nil?
21
21
 
22
22
  if format.nil?
23
23
  format = Moose::Inventory::Config._confopts[:format].downcase
24
24
  end
25
-
25
+
26
26
  case format
27
- when 'yaml','y'
27
+ when 'yaml', 'y'
28
28
  $stdout.puts arg.to_yaml
29
29
 
30
- when 'prettyjson','pjson','p'
30
+ when 'prettyjson', 'pjson', 'p'
31
31
  $stdout.puts JSON.pretty_generate(arg)
32
32
 
33
- when 'json','j'
33
+ when 'json', 'j'
34
34
  $stdout.puts arg.to_json
35
35
 
36
36
  else
37
37
  abort("Output format '#{format}' is not yet supported.")
38
38
  end
39
39
  end
40
-
40
+
41
41
  #---------------
42
42
  attr_accessor :indent
43
43
 
44
44
  def reset_indent
45
45
  @indent = 2
46
46
  end
47
-
48
- def puts(indent, msg, stream='STDOUT')
47
+
48
+ def puts(indent, msg, stream = 'STDOUT')
49
49
  case stream
50
50
  when 'STDOUT'
51
51
  $stdout.puts msg.indent(indent)
@@ -55,8 +55,8 @@ module Moose
55
55
  abort("Output stream '#{stream}' is not known.")
56
56
  end
57
57
  end
58
-
59
- def print(indent, msg, stream='STDOUT')
58
+
59
+ def print(indent, msg, stream = 'STDOUT')
60
60
  case stream
61
61
  when 'STDOUT'
62
62
  $stdout.print msg.indent(indent)
@@ -67,25 +67,24 @@ module Moose
67
67
  end
68
68
  end
69
69
 
70
- def info(indent, msg, stream='STDOUT')
70
+ def info(_indent, _msg, stream = 'STDOUT')
71
71
  case stream
72
72
  when 'STDOUT'
73
- $stdout.print "INFO: {msg}"
73
+ $stdout.print 'INFO: {msg}'
74
74
  when 'STDERR'
75
- $stderr.print "INFO: {msg}"
75
+ $stderr.print 'INFO: {msg}'
76
76
  else
77
77
  abort("Output stream '#{stream}' is not known.")
78
78
  end
79
79
  end
80
-
80
+
81
81
  def warn(msg)
82
- $stderr.print "WARNING: #{msg}"
82
+ $stderr.print "WARNING: #{msg}"
83
83
  end
84
84
 
85
85
  def error(msg)
86
- $stderr.print "ERROR: #{msg}"
86
+ $stderr.print "ERROR: #{msg}"
87
87
  end
88
-
89
88
  end
90
89
  end
91
90
  end
@@ -1,12 +1,15 @@
1
1
  require 'thor'
2
2
  require_relative './formatter.rb'
3
+ require_relative './helpers.rb'
3
4
 
4
5
  module Moose
5
6
  module Inventory
6
7
  module Cli
7
8
  ##
8
9
  # Class implementing the "group" methods of the CLI
9
- class Group < Thor
10
+ class Group < Thor
11
+ include Moose::Inventory::Cli::Helpers
12
+
10
13
  require_relative 'group_add'
11
14
  require_relative 'group_get'
12
15
  require_relative 'group_list'
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require_relative './formatter.rb'
4
+ require_relative 'formatter'
5
+ require_relative '../inventory_context'
6
+ require_relative '../operations/add_groups'
3
7
 
4
8
  module Moose
5
9
  module Inventory
@@ -10,89 +14,99 @@ module Moose
10
14
  #==========================
11
15
  desc 'add NAME', 'Add a group NAME to the inventory'
12
16
  option :hosts
13
- # rubocop:disable Metrics/LineLength
14
- def add(*argv) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
15
- # rubocop:enable Metrics/LineLength
16
- if argv.length < 1
17
- abort("ERROR: Wrong number of arguments, #{argv.length} for 1 or more.")
18
- end
17
+ def add(*argv)
18
+ abort_if_missing_args(argv, 1, '1 or more')
19
19
 
20
- # Arguments
21
- names = argv.uniq.map(&:downcase)
22
- options[:hosts] = '' if options[:hosts].nil?
23
- hosts = options[:hosts].downcase.split(',').uniq
20
+ names = normalize_names(argv)
21
+ hosts = csv_option_names(options[:hosts])
24
22
 
25
- # sanity
26
- if names.include?('ungrouped')
27
- abort("ERROR: Cannot manually manipulate the automatic group 'ungrouped'\n")
28
- end
23
+ abort_if_automatic_group(
24
+ names,
25
+ "ERROR: Cannot manually manipulate the automatic group 'ungrouped'\n"
26
+ )
27
+
28
+ result = Moose::Inventory::Operations::AddGroups
29
+ .new(context: Moose::Inventory::InventoryContext.new(db: db))
30
+ .call(names: names, hosts: hosts)
31
+ render_add_groups_events(result.events)
29
32
 
30
- # Convenience
31
- db = Moose::Inventory::DB
32
- fmt = Moose::Inventory::Cli::Formatter
33
-
34
- # Transaction
35
- warn_count = 0
36
- db.transaction do # Transaction start
37
- names.each do |name|
38
- # Add the group
39
- puts "Add group '#{name}':"
40
- group = db.models[:group].find(name: name)
41
- hosts_ds = nil
42
- fmt.puts 2, "- create group..."
43
- if group.nil?
44
- group = db.models[:group].create(name: name)
45
- fmt.puts 4, '- OK'
46
- else
47
- warn_count += 1
48
- fmt.warn "Group '#{name}' already exists, skipping creation.\n"
49
- fmt.puts 4, "- already exists, skipping."
50
- hosts_ds = group.hosts_dataset
51
- fmt.puts 4, '- OK'
52
- end
53
-
54
- # Associate with hosts
55
- hosts.each do |h|
56
- next if h.nil? || h.empty?
57
- fmt.puts 2, "- add association {group:#{name} <-> host:#{ h }}..."
58
- host = db.models[:host].find(name: h)
59
- if host.nil?
60
- warn_count += 1
61
- fmt.warn "Host '#{h}' doesn't exist, but will be created.\n"
62
- fmt.puts 4, "- host doesn't exist, creating now..."
63
- host = db.models[:host].create(name: h)
64
- fmt.puts 6, "- OK"
65
- end
66
- if !hosts_ds.nil? && !hosts_ds[name: h].nil?
67
- warn_count += 1
68
- fmt.warn "Association {group:#{name} <-> host:#{ h }}"\
69
- " already exists, skipping creation.\n"
70
- fmt.puts 4, "- already exists, skipping."
71
- else
72
- group.add_host(host)
73
- end
74
- fmt.puts 4, '- OK'
75
-
76
- # Handle the host's automatic 'ungrouped' group
77
- ungrouped = host.groups_dataset[name: 'ungrouped']
78
- unless ungrouped.nil?
79
- fmt.puts 2, "- remove automatic association {group:ungrouped"\
80
- " <-> host:#{ h }}..."
81
- host.remove_group( ungrouped ) unless ungrouped.nil?
82
- fmt.puts 4, '- OK'
83
- end
84
- end
85
- fmt.puts 2, '- all OK'
86
- end
87
- end # Transaction end
88
- if warn_count == 0
33
+ if result.warning_count.zero?
89
34
  puts 'Succeeded'
90
35
  else
91
36
  puts 'Succeeded, with warnings.'
92
37
  end
93
38
  end
39
+
40
+ private
41
+
42
+ def render_add_groups_events(events)
43
+ events.each { |event| render_add_groups_event(event) }
44
+ end
45
+
46
+ def render_add_groups_event(event)
47
+ payload = event.payload
48
+
49
+ return render_add_groups_event_puts(event.type, payload) if puts_event?(event.type)
50
+ return render_add_groups_event_warn(event.type, payload) if warn_event?(event.type)
51
+
52
+ render_add_groups_event_fmt(event.type, payload)
53
+ end
54
+
55
+ def puts_event?(type)
56
+ type == :group_started
57
+ end
58
+
59
+ def warn_event?(type)
60
+ %i[group_exists host_missing_created association_exists].include?(type)
61
+ end
62
+
63
+ def render_add_groups_event_puts(type, payload)
64
+ puts "Add group '#{payload[:name]}':" if type == :group_started
65
+ end
66
+
67
+ def render_add_groups_event_warn(type, payload)
68
+ case type
69
+ when :group_exists
70
+ fmt.warn "Group '#{payload[:name]}' already exists, skipping creation.\n"
71
+ when :host_missing_created
72
+ fmt.warn "Host '#{payload[:name]}' doesn't exist, but will be created.\n"
73
+ when :association_exists
74
+ fmt.warn(
75
+ "Association {group:#{payload[:group]} <-> host:#{payload[:host]}} " \
76
+ "already exists, skipping creation.\n"
77
+ )
78
+ end
79
+ end
80
+
81
+ def render_add_groups_event_fmt(type, payload)
82
+ return render_add_groups_event_status(type, payload) if status_event?(type)
83
+
84
+ case type
85
+ when :creating_group
86
+ fmt.puts 2, '- create group...'
87
+ when :adding_association
88
+ fmt.puts 2, "- add association {group:#{payload[:group]} <-> host:#{payload[:host]}}..."
89
+ when :host_creating_now
90
+ fmt.puts 4, '- host doesn\'t exist, creating now...'
91
+ when :removing_automatic_group
92
+ fmt.puts 2, "- remove automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
93
+ when :group_complete
94
+ fmt.puts 2, '- all OK'
95
+ end
96
+ end
97
+
98
+ def status_event?(type)
99
+ %i[already_exists_skipping ok].include?(type)
100
+ end
101
+
102
+ def render_add_groups_event_status(type, payload)
103
+ if type == :already_exists_skipping
104
+ fmt.puts payload[:indent], '- already exists, skipping.'
105
+ else
106
+ fmt.puts payload[:indent], '- OK'
107
+ end
108
+ end
94
109
  end
95
110
  end
96
111
  end
97
112
  end
98
-