cem_acpt 0.11.0 → 0.11.2
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/.gitignore +8 -0
- data/.worktreeinclude +1 -0
- data/CLAUDE.md +64 -25
- data/Gemfile.lock +1 -1
- data/README.md +20 -7
- data/docs/ARCHITECTURE.md +1042 -0
- data/docs/rfcs/0000-template.md +54 -0
- data/docs/rfcs/0001-fix-bolt-missing-skip-path.md +105 -0
- data/docs/rfcs/0002-fix-default-character-substitutions.md +119 -0
- data/docs/rfcs/0003-windows-image-builder-template.md +110 -0
- data/docs/rfcs/0004-image-name-truncation-off-by-one.md +108 -0
- data/docs/rfcs/0005-os-dispatch-replace-windows-heuristic.md +117 -0
- data/docs/rfcs/0006-configurable-windows-bucket.md +96 -0
- data/docs/rfcs/0007-logging-quiet-and-typos.md +121 -0
- data/docs/rfcs/0008-namespace-platform-classes.md +110 -0
- data/docs/rfcs/0009-bolt-log-formatter-cleanup.md +111 -0
- data/docs/rfcs/0010-dead-code-cleanup.md +83 -0
- data/docs/rfcs/0011-provisioner-factory-consistency.md +89 -0
- data/docs/rfcs/README.md +34 -0
- data/lib/cem_acpt/cli.rb +10 -1
- data/lib/cem_acpt/config/cem_acpt.rb +4 -1
- data/lib/cem_acpt/image_builder/errors.rb +24 -0
- data/lib/cem_acpt/image_builder/provision_commands.rb +15 -3
- data/lib/cem_acpt/image_builder.rb +29 -2
- data/lib/cem_acpt/image_name_builder.rb +8 -1
- data/lib/cem_acpt/platform/gcp.rb +112 -106
- data/lib/cem_acpt/platform.rb +21 -19
- data/lib/cem_acpt/provision/terraform/linux.rb +1 -1
- data/lib/cem_acpt/provision/terraform/os_data.rb +23 -0
- data/lib/cem_acpt/provision/terraform/windows.rb +7 -1
- data/lib/cem_acpt/provision/terraform.rb +20 -16
- data/lib/cem_acpt/test_runner/log_formatter/bolt_summary_results_formatter.rb +2 -1
- data/lib/cem_acpt/test_runner/log_formatter.rb +0 -1
- data/lib/cem_acpt/test_runner.rb +21 -8
- data/lib/cem_acpt/utils/winrm_runner.rb +4 -3
- data/lib/cem_acpt/utils.rb +0 -12
- data/lib/cem_acpt/version.rb +1 -1
- data/lib/cem_acpt.rb +19 -7
- data/lib/terraform/gcp/linux/main.tf +6 -1
- data/lib/terraform/image/gcp/linux/main.tf +8 -1
- data/specifications/CEM-6713.md +165 -0
- data/specifications/CEM-6714.md +271 -0
- data/specifications/CEM-6715.md +133 -0
- data/specifications/CEM-6716.md +160 -0
- data/specifications/CEM-6717.md +239 -0
- data/specifications/CEM-6718.md +120 -0
- data/specifications/CEM-6719.md +173 -0
- metadata +26 -11
- data/.claude/settings.local.json +0 -7
- data/lib/cem_acpt/action_result.rb +0 -91
- data/lib/cem_acpt/puppet_helpers.rb +0 -38
- data/lib/cem_acpt/test_runner/log_formatter/bolt_error_formatter.rb +0 -65
- data/lib/cem_acpt/test_runner/log_formatter/bolt_output_formatter.rb +0 -54
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# RFC 0006: Make the Windows GCS bucket configurable
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Author:** TBD
|
|
5
|
+
- **Created:** 2026-04-30
|
|
6
|
+
- **Priority:** High
|
|
7
|
+
- **Category:** Bug
|
|
8
|
+
- **Affected components:** `lib/cem_acpt/test_runner.rb`, `lib/cem_acpt/config/cem_acpt.rb`
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
The Windows test path uploads the Puppet module tarball to a
|
|
13
|
+
hard-coded `gs://win_cem_acpt` bucket. Anyone who is not a Puppet
|
|
14
|
+
employee (or who has a different bucket name in their own GCP project)
|
|
15
|
+
must fork the gem to use the Windows path. Make the bucket name
|
|
16
|
+
configurable.
|
|
17
|
+
|
|
18
|
+
## Background
|
|
19
|
+
|
|
20
|
+
`lib/cem_acpt/test_runner.rb:386`:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
@run_data[:win_remote_module_path] =
|
|
24
|
+
File.join('gs://win_cem_acpt', @run_data[:win_remote_module_name])
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The same string appears in `cleanup_bucket` (line 397+).
|
|
28
|
+
[`docs/ARCHITECTURE.md` §13](../ARCHITECTURE.md#13-windows-path) calls
|
|
29
|
+
this out as not configurable.
|
|
30
|
+
|
|
31
|
+
## Problem
|
|
32
|
+
|
|
33
|
+
- External users cannot run the Windows acceptance suite at all.
|
|
34
|
+
- There is no way to point at a per-project bucket without editing
|
|
35
|
+
vendored gem source.
|
|
36
|
+
- A typo or bucket-name change requires a new gem release.
|
|
37
|
+
|
|
38
|
+
## Proposal
|
|
39
|
+
|
|
40
|
+
Add a config key `platform.gcp.windows_bucket` (under the existing
|
|
41
|
+
`platform` namespace), defaulting to `win_cem_acpt` to preserve
|
|
42
|
+
current behavior for Puppet contributors:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
# lib/cem_acpt/config/cem_acpt.rb defaults
|
|
46
|
+
platform: {
|
|
47
|
+
name: 'gcp',
|
|
48
|
+
gcp: {
|
|
49
|
+
windows_bucket: 'win_cem_acpt',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
In `test_runner.rb`, replace literals with config lookups:
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
def windows_bucket_uri
|
|
58
|
+
bucket = config.get('platform.gcp.windows_bucket')
|
|
59
|
+
raise 'platform.gcp.windows_bucket is not configured' if bucket.nil? || bucket.empty?
|
|
60
|
+
"gs://#{bucket}"
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use `windows_bucket_uri` in both `upload_module_to_bucket` and
|
|
65
|
+
`cleanup_bucket`.
|
|
66
|
+
|
|
67
|
+
Also surface a `--windows-bucket BUCKET` CLI flag for ad-hoc overrides
|
|
68
|
+
that mirror existing flag-based knobs.
|
|
69
|
+
|
|
70
|
+
## Alternatives Considered
|
|
71
|
+
|
|
72
|
+
- **Derive the bucket name from `platform.project`.** Avoids a new
|
|
73
|
+
config key but requires every project to follow the same naming
|
|
74
|
+
convention, which is its own coupling.
|
|
75
|
+
- **Drop the bucket-upload step entirely** in favor of a WinRM file
|
|
76
|
+
push. Larger change; would also remove a useful "the module
|
|
77
|
+
artifact is reachable from any node" property.
|
|
78
|
+
|
|
79
|
+
## Risks & Migration
|
|
80
|
+
|
|
81
|
+
- Defaulting to `win_cem_acpt` means existing internal users see no
|
|
82
|
+
change.
|
|
83
|
+
- External users gain a new required override (or use the default
|
|
84
|
+
if their bucket happens to share the name).
|
|
85
|
+
- Config-explanation output (`-X`) will start listing the new key.
|
|
86
|
+
|
|
87
|
+
## Acceptance Criteria
|
|
88
|
+
|
|
89
|
+
- [ ] Config schema (`VALID_KEYS` and `defaults`) extended.
|
|
90
|
+
- [ ] CLI flag `--windows-bucket` parsed in `Cli`.
|
|
91
|
+
- [ ] All references to `'gs://win_cem_acpt'` removed from
|
|
92
|
+
`test_runner.rb`.
|
|
93
|
+
- [ ] RSpec coverage for both upload and cleanup paths.
|
|
94
|
+
- [ ] [`docs/ARCHITECTURE.md` §13](../ARCHITECTURE.md#13-windows-path)
|
|
95
|
+
updated to describe the new key.
|
|
96
|
+
- [ ] README updated.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# RFC 0007: Tighten `--quiet` semantics and fix logging typos
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Author:** TBD
|
|
5
|
+
- **Created:** 2026-04-30
|
|
6
|
+
- **Priority:** High
|
|
7
|
+
- **Category:** Bug
|
|
8
|
+
- **Affected components:** `lib/cem_acpt.rb`, `lib/cem_acpt/test_runner.rb`
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`--quiet` is silently a no-op when used without `--log-file`, and a
|
|
13
|
+
log line in `upload_module_to_bucket` references a non-existent
|
|
14
|
+
`@run_data[:module_pakage_path]` key (typo for `module_package_path`).
|
|
15
|
+
Fix both. Document the CI-mode override.
|
|
16
|
+
|
|
17
|
+
## Background
|
|
18
|
+
|
|
19
|
+
`lib/cem_acpt.rb#initialize_logger!` (lines 59-80):
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
logdevs = [$stdout]
|
|
23
|
+
if config.get('log_file') && config.get('quiet')
|
|
24
|
+
logdevs = [config.get('log_file')]
|
|
25
|
+
elsif config.get('log_file') && !config.get('quiet')
|
|
26
|
+
logdevs << config.get('log_file')
|
|
27
|
+
end
|
|
28
|
+
if config.ci_mode? && !logdevs.include?($stdout)
|
|
29
|
+
logdevs << $stdout
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
[`docs/ARCHITECTURE.md` §15 / §19 (item 6)](../ARCHITECTURE.md#15-logging)
|
|
34
|
+
describes the current behavior and flags the surprising "quiet alone
|
|
35
|
+
does nothing" case.
|
|
36
|
+
|
|
37
|
+
`test_runner.rb:388`:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
logger.info('CemAcpt') {
|
|
41
|
+
"Uploading #{@run_data[:module_pakage_path]} to #{@run_data[:win_remote_module_path]}..."
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`@run_data[:module_pakage_path]` is never assigned. The interpolation
|
|
46
|
+
quietly produces an empty string in the log. The other call on the
|
|
47
|
+
following line correctly reads `module_package_path`.
|
|
48
|
+
|
|
49
|
+
## Problem
|
|
50
|
+
|
|
51
|
+
1. `--quiet` without `--log-file` reads as "be quiet" but does
|
|
52
|
+
nothing — confusing for anyone scripting around it.
|
|
53
|
+
2. `--quiet --log-file X` *with* CI mode also adds `$stdout` back in;
|
|
54
|
+
undocumented in `--help`.
|
|
55
|
+
3. Misspelled run-data key produces a broken log line — no functional
|
|
56
|
+
damage today, but the log message is one of the few breadcrumbs
|
|
57
|
+
for diagnosing the Windows upload step.
|
|
58
|
+
|
|
59
|
+
## Proposal
|
|
60
|
+
|
|
61
|
+
### Quiet semantics
|
|
62
|
+
|
|
63
|
+
Treat `--quiet` as "drop `$stdout`" everywhere except CI mode, with
|
|
64
|
+
clear messaging:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
def initialize_logger!
|
|
68
|
+
raise 'Config must be loaded ...' if config.nil? || config.empty?
|
|
69
|
+
|
|
70
|
+
logdevs = []
|
|
71
|
+
logdevs << $stdout unless config.get('quiet')
|
|
72
|
+
logdevs << config.get('log_file') if config.get('log_file')
|
|
73
|
+
if config.ci_mode? && !logdevs.include?($stdout)
|
|
74
|
+
logger_warn_once('--quiet is overridden in CI mode; logging to stdout for ::group:: support')
|
|
75
|
+
logdevs << $stdout
|
|
76
|
+
end
|
|
77
|
+
raise '--quiet without --log-file would silence all output; pass --log-file or drop --quiet' if logdevs.empty?
|
|
78
|
+
...
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Refusing the all-silenced combination outside CI is preferred to
|
|
83
|
+
silently dropping logs.
|
|
84
|
+
|
|
85
|
+
### Typo
|
|
86
|
+
|
|
87
|
+
In `test_runner.rb:388`, change `:module_pakage_path` to
|
|
88
|
+
`:module_package_path`. Add a smoke test that exercises
|
|
89
|
+
`upload_module_to_bucket` with a stub shell to assert the log line
|
|
90
|
+
contains the correct path.
|
|
91
|
+
|
|
92
|
+
### Documentation
|
|
93
|
+
|
|
94
|
+
Update `docs/ARCHITECTURE.md` §15 to reflect the new behavior, and
|
|
95
|
+
add a one-line note in `Cli` help next to `--quiet`.
|
|
96
|
+
|
|
97
|
+
## Alternatives Considered
|
|
98
|
+
|
|
99
|
+
- **Leave `--quiet` alone.** Documented as "no effect alone" — but
|
|
100
|
+
that's exactly the surprise we want to remove.
|
|
101
|
+
- **Promote `--quiet` to fully silence logs even in CI.** Breaks
|
|
102
|
+
`::group::` annotations on Actions; not desirable.
|
|
103
|
+
|
|
104
|
+
## Risks & Migration
|
|
105
|
+
|
|
106
|
+
- Users today who passed `--quiet` and expected stdout to remain will
|
|
107
|
+
now see stdout disappear (correctly). This is a deliberate
|
|
108
|
+
behavioral fix.
|
|
109
|
+
- Users today who passed `--quiet` with no `--log-file` get an
|
|
110
|
+
immediate, clear error rather than running normally with stdout
|
|
111
|
+
intact.
|
|
112
|
+
|
|
113
|
+
## Acceptance Criteria
|
|
114
|
+
|
|
115
|
+
- [ ] `lib/cem_acpt.rb#initialize_logger!` updated; comment cites
|
|
116
|
+
this RFC.
|
|
117
|
+
- [ ] `:module_pakage_path` typo fixed.
|
|
118
|
+
- [ ] RSpec covers the four logging combinations (`-q`, `-q --log-file`,
|
|
119
|
+
`--log-file` alone, `-q` in CI mode).
|
|
120
|
+
- [ ] `docs/ARCHITECTURE.md` §15 updated.
|
|
121
|
+
- [ ] `--help` output mentions the CI override.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# RFC 0008: Namespace dynamically-created platform classes
|
|
2
|
+
|
|
3
|
+
- **Status:** Implemented (CEM-6717)
|
|
4
|
+
- **Author:** TBD
|
|
5
|
+
- **Created:** 2026-04-30
|
|
6
|
+
- **Priority:** Medium
|
|
7
|
+
- **Category:** Refactor
|
|
8
|
+
- **Affected components:** `lib/cem_acpt/platform.rb`, `lib/cem_acpt/platform/gcp.rb`
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`Platform.platform_class` registers each generated class as a
|
|
13
|
+
**top-level** constant (e.g. `::Gcp`). This pollutes `Object`'s
|
|
14
|
+
constant table and risks silent collisions with any same-named class
|
|
15
|
+
introduced elsewhere — including by transitive dependencies. Move the
|
|
16
|
+
generated class into the `CemAcpt::Platform` namespace.
|
|
17
|
+
|
|
18
|
+
## Background
|
|
19
|
+
|
|
20
|
+
`lib/cem_acpt/platform.rb:79-115`:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
def platform_class(base_type, platform)
|
|
24
|
+
class_name = platform.capitalize
|
|
25
|
+
...
|
|
26
|
+
if Object.const_defined?(class_name)
|
|
27
|
+
klass = Object.const_get(class_name)
|
|
28
|
+
else
|
|
29
|
+
...
|
|
30
|
+
klass = Object.const_set(class_name, Class.new(baseklass) do
|
|
31
|
+
require_relative "platform/#{platform}"
|
|
32
|
+
include Platform
|
|
33
|
+
end)
|
|
34
|
+
end
|
|
35
|
+
klass
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
[`docs/ARCHITECTURE.md` §6 / §19 (item 5)](../ARCHITECTURE.md#6-platform-abstraction)
|
|
40
|
+
flags this as a defensive-namespace concern.
|
|
41
|
+
|
|
42
|
+
## Problem
|
|
43
|
+
|
|
44
|
+
- A future contributor (or a gem we depend on) defining
|
|
45
|
+
`class ::Gcp` would silently win the cache check
|
|
46
|
+
`Object.const_defined?('Gcp')`, so `platform_class` would return
|
|
47
|
+
the wrong class.
|
|
48
|
+
- The constant lookup is also case-sensitive and the cache key
|
|
49
|
+
`platform.capitalize` fails for multi-word platforms like
|
|
50
|
+
`aws_govcloud` (would become `Aws_govcloud`).
|
|
51
|
+
- Top-level constants make the runtime class graph noisier than it
|
|
52
|
+
needs to be.
|
|
53
|
+
|
|
54
|
+
## Proposal
|
|
55
|
+
|
|
56
|
+
Register and look up the class under `CemAcpt::Platform`:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
def platform_class(base_type, platform)
|
|
60
|
+
const_name = platform.split(/[_-]/).map(&:capitalize).join
|
|
61
|
+
|
|
62
|
+
if CemAcpt::Platform.const_defined?(const_name, false)
|
|
63
|
+
return CemAcpt::Platform.const_get(const_name, false)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
baseklass = case base_type.to_sym
|
|
67
|
+
when :base then CemAcpt::Platform::Base
|
|
68
|
+
when :test then CemAcpt::Platform::TestBase
|
|
69
|
+
else raise Error, "Base type #{base_type} is not supported"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
CemAcpt::Platform.const_set(const_name, Class.new(baseklass) do
|
|
73
|
+
require_relative "platform/#{platform}"
|
|
74
|
+
include CemAcpt::Platform.const_get(:Platform, false) # documented hook
|
|
75
|
+
end)
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Notes:
|
|
80
|
+
|
|
81
|
+
- `const_defined?(name, false)` skips ancestor lookup, eliminating the
|
|
82
|
+
collision.
|
|
83
|
+
- The `split(...).map(&:capitalize).join` form camel-cases multi-word
|
|
84
|
+
platform names safely.
|
|
85
|
+
- `include Platform` references the module from the loaded file; we
|
|
86
|
+
preserve that contract but resolve it relative to the new namespace.
|
|
87
|
+
|
|
88
|
+
## Alternatives Considered
|
|
89
|
+
|
|
90
|
+
- **Switch to a registration DSL** (`Platform.register :gcp do ... end`).
|
|
91
|
+
Cleaner long term but a wider refactor. Defer.
|
|
92
|
+
- **Keep top-level but assert uniqueness.** Solves the collision
|
|
93
|
+
problem but doesn't address the namespace pollution.
|
|
94
|
+
|
|
95
|
+
## Risks & Migration
|
|
96
|
+
|
|
97
|
+
- Any code that referenced the top-level `::Gcp` (none in this repo
|
|
98
|
+
per grep) would need to change. There are no such references in
|
|
99
|
+
`lib/`, `exe/`, or `spec/`.
|
|
100
|
+
- The base class `CemAcpt::Platform::Base` remains untouched.
|
|
101
|
+
|
|
102
|
+
## Acceptance Criteria
|
|
103
|
+
|
|
104
|
+
- [ ] `Platform.platform_class` registers under `CemAcpt::Platform`.
|
|
105
|
+
- [ ] No top-level `::Gcp` constant is created at runtime
|
|
106
|
+
(verified via spec).
|
|
107
|
+
- [ ] Multi-word platform names (e.g. `azure_arm`) round-trip
|
|
108
|
+
correctly.
|
|
109
|
+
- [ ] [`docs/ARCHITECTURE.md` §6](../ARCHITECTURE.md#6-platform-abstraction)
|
|
110
|
+
and §19 updated.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# RFC 0009: Wire up or remove the orphaned Bolt log formatters
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Author:** TBD
|
|
5
|
+
- **Created:** 2026-04-30
|
|
6
|
+
- **Priority:** Medium
|
|
7
|
+
- **Category:** Cleanup
|
|
8
|
+
- **Affected components:** `lib/cem_acpt/test_runner/log_formatter.rb`, `lib/cem_acpt/test_runner/log_formatter/`
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
Two formatter files exist under `lib/cem_acpt/test_runner/log_formatter/`
|
|
13
|
+
but are never matched by `LogFormatter.new_formatter`'s `case`.
|
|
14
|
+
`bolt_output_formatter.rb` is `require_relative`'d but unused;
|
|
15
|
+
`bolt_error_formatter.rb` is neither required nor referenced. Either
|
|
16
|
+
they should be hooked into the dispatcher or deleted.
|
|
17
|
+
|
|
18
|
+
## Background
|
|
19
|
+
|
|
20
|
+
`lib/cem_acpt/test_runner/log_formatter.rb`:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
require_relative 'log_formatter/bolt_summary_results_formatter'
|
|
24
|
+
require_relative 'log_formatter/bolt_output_formatter' # required, not matched
|
|
25
|
+
require_relative 'log_formatter/goss_action_response'
|
|
26
|
+
require_relative 'log_formatter/goss_error_formatter'
|
|
27
|
+
require_relative 'log_formatter/standard_error_formatter'
|
|
28
|
+
# bolt_error_formatter.rb is NOT required
|
|
29
|
+
|
|
30
|
+
module ...
|
|
31
|
+
def self.new_formatter(result, *args, **_kwargs)
|
|
32
|
+
case result
|
|
33
|
+
when CemAcpt::Goss::Api::ActionResponse then ...
|
|
34
|
+
when CemAcpt::Bolt::SummaryResults then BoltSummaryResultsFormatter.new(...)
|
|
35
|
+
when StandardError then StandardErrorFormatter.new(result)
|
|
36
|
+
else raise ArgumentError, ...
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
[`docs/ARCHITECTURE.md` §11 / §19 (item 8)](../ARCHITECTURE.md#11-result-aggregation--log-formatting)
|
|
43
|
+
flags both files.
|
|
44
|
+
|
|
45
|
+
## Problem
|
|
46
|
+
|
|
47
|
+
- Future contributors waste time deciding whether the formatters are
|
|
48
|
+
load-bearing. The `require_relative` for `bolt_output_formatter`
|
|
49
|
+
creates the false impression that they are.
|
|
50
|
+
- If `BoltOutputFormatter` was meant to render
|
|
51
|
+
`Bolt::Cmd::Output` instances, the dispatcher silently falls through
|
|
52
|
+
to `StandardErrorFormatter` (or raises) for any caller that pushes
|
|
53
|
+
one onto the results queue.
|
|
54
|
+
|
|
55
|
+
## Proposal
|
|
56
|
+
|
|
57
|
+
Pick one of two paths after a 5-minute decision:
|
|
58
|
+
|
|
59
|
+
### Option A — wire them up
|
|
60
|
+
|
|
61
|
+
If `BoltOutputFormatter` / `BoltErrorFormatter` are intended to render
|
|
62
|
+
`Bolt::Cmd::Output` and `Bolt::Cmd::OutputError`:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
require_relative 'log_formatter/bolt_error_formatter'
|
|
66
|
+
|
|
67
|
+
case result
|
|
68
|
+
...
|
|
69
|
+
when CemAcpt::Bolt::Cmd::OutputError
|
|
70
|
+
BoltErrorFormatter.new(result)
|
|
71
|
+
when CemAcpt::Bolt::Cmd::Output
|
|
72
|
+
BoltOutputFormatter.new(*args, subject: result)
|
|
73
|
+
when CemAcpt::Bolt::SummaryResults
|
|
74
|
+
BoltSummaryResultsFormatter.new(*args, subject: result)
|
|
75
|
+
...
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Add coverage in
|
|
80
|
+
`spec/cem_acpt/test_runner/log_formatter/` mirroring the existing
|
|
81
|
+
`goss_action_response_spec.rb`.
|
|
82
|
+
|
|
83
|
+
### Option B — delete them
|
|
84
|
+
|
|
85
|
+
If, as the current code suggests, only the summary formatter is
|
|
86
|
+
needed, delete both files and the dangling `require_relative`. Add
|
|
87
|
+
a one-line comment on `BoltSummaryResultsFormatter` documenting that
|
|
88
|
+
it is the canonical Bolt formatter.
|
|
89
|
+
|
|
90
|
+
## Recommendation
|
|
91
|
+
|
|
92
|
+
Option B. The dispatcher has been shipping in this state for at least
|
|
93
|
+
several releases without anyone noticing the absent cases — the
|
|
94
|
+
formatters are aspirational dead code. Deleting them now removes the
|
|
95
|
+
ambiguity; if the need re-emerges, reintroducing them is a small
|
|
96
|
+
patch.
|
|
97
|
+
|
|
98
|
+
## Risks & Migration
|
|
99
|
+
|
|
100
|
+
- Public API: none. The formatter classes are not part of the gem's
|
|
101
|
+
documented API.
|
|
102
|
+
- Tests: none of the existing specs reference either file.
|
|
103
|
+
|
|
104
|
+
## Acceptance Criteria
|
|
105
|
+
|
|
106
|
+
- [ ] Decision recorded (Option A or B).
|
|
107
|
+
- [ ] If A: dispatcher updated, both files required, RSpec coverage
|
|
108
|
+
added.
|
|
109
|
+
- [ ] If B: both files deleted, dangling `require_relative` removed.
|
|
110
|
+
- [ ] [`docs/ARCHITECTURE.md` §11](../ARCHITECTURE.md#11-result-aggregation--log-formatting)
|
|
111
|
+
and §19 updated.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# RFC 0010: Remove unreferenced top-level helpers and Windows placeholder
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Author:** TBD
|
|
5
|
+
- **Created:** 2026-04-30
|
|
6
|
+
- **Priority:** Medium
|
|
7
|
+
- **Category:** Cleanup
|
|
8
|
+
- **Affected components:** `lib/cem_acpt/puppet_helpers.rb`, `lib/cem_acpt/action_result.rb`, `lib/cem_acpt/provision/terraform/windows.rb`, `lib/cem_acpt/utils.rb`
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
Several files / methods are dead — defined, but never called from
|
|
13
|
+
`lib/`, `exe/`, or `spec/`. They confuse the codebase map and
|
|
14
|
+
contradict the actual architecture. Remove them.
|
|
15
|
+
|
|
16
|
+
## Background
|
|
17
|
+
|
|
18
|
+
[`docs/ARCHITECTURE.md` §19 (items 1, 2, 3)](../ARCHITECTURE.md#19-open-questions--observed-dead-code)
|
|
19
|
+
identifies the following:
|
|
20
|
+
|
|
21
|
+
| File / Symbol | Status |
|
|
22
|
+
|-------------------------------------------------------------|----------------------------------------------|
|
|
23
|
+
| `lib/cem_acpt/puppet_helpers.rb` (`PuppetHelpers::Module.build_module_package`) | Superseded by `Utils::Puppet::ModulePackageBuilder`. No callers. |
|
|
24
|
+
| `lib/cem_acpt/action_result.rb` (`CemAcpt::ActionResult`, `CemAcpt::ErrorActionResult`) | Superseded by `TestRunner::TestResults::TestActionResult` / `TestErrorActionResult`. No callers. |
|
|
25
|
+
| `Provision::Windows#provision_commands` returning `['placeholder']` | Inert; the Windows path runs PowerShell from Ruby, not from a `remote-exec`. |
|
|
26
|
+
| `Utils.package_win_module` | Referenced by ARCHITECTURE.md as unused; re-confirm before removing. |
|
|
27
|
+
|
|
28
|
+
## Problem
|
|
29
|
+
|
|
30
|
+
- Each of these reads as "load-bearing" to a contributor wandering
|
|
31
|
+
the tree.
|
|
32
|
+
- The placeholder return value in `Provision::Windows` is the kind of
|
|
33
|
+
code that gets "fixed" by a future contributor who doesn't realize
|
|
34
|
+
the Windows path skips the `remote-exec` block entirely.
|
|
35
|
+
- `action_result.rb` defines two top-level constants
|
|
36
|
+
(`CemAcpt::ActionResult`, `CemAcpt::ErrorActionResult`) that shadow
|
|
37
|
+
intuitive names a future contributor might want.
|
|
38
|
+
|
|
39
|
+
## Proposal
|
|
40
|
+
|
|
41
|
+
1. **Delete** `lib/cem_acpt/puppet_helpers.rb`. Audit specs/fixtures
|
|
42
|
+
first; remove any orphan requires.
|
|
43
|
+
2. **Delete** `lib/cem_acpt/action_result.rb`. Same audit.
|
|
44
|
+
3. **In `Provision::Windows`**, replace the placeholder method with
|
|
45
|
+
a comment documenting that the Windows path intentionally has no
|
|
46
|
+
`remote-exec` provisioner, and have `provision_commands` raise
|
|
47
|
+
`NotImplementedError` if called by accident:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
def provision_commands
|
|
51
|
+
raise NotImplementedError,
|
|
52
|
+
'Windows provisioning is performed via Utils::WinRMRunner, ' \
|
|
53
|
+
'not via Terraform remote-exec. See ARCHITECTURE.md §13.'
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. **`Utils.package_win_module`**: confirm with `git grep` and remove
|
|
58
|
+
if there are no callers. (One pass at the start of this RFC's
|
|
59
|
+
implementation should suffice.)
|
|
60
|
+
|
|
61
|
+
## Alternatives Considered
|
|
62
|
+
|
|
63
|
+
- **Mark as deprecated and keep around for one release.** Overkill —
|
|
64
|
+
these are internal helpers, not public API.
|
|
65
|
+
- **Move to a `legacy/` directory.** Just delays the cleanup.
|
|
66
|
+
|
|
67
|
+
## Risks & Migration
|
|
68
|
+
|
|
69
|
+
- These symbols are not part of the public API of the gem; removing
|
|
70
|
+
them does not require a major version bump.
|
|
71
|
+
- A `git grep` for each symbol must come back empty before deletion.
|
|
72
|
+
|
|
73
|
+
## Acceptance Criteria
|
|
74
|
+
|
|
75
|
+
- [ ] `git grep PuppetHelpers` returns no matches under `lib/`,
|
|
76
|
+
`exe/`, or `spec/`; file deleted.
|
|
77
|
+
- [ ] `git grep CemAcpt::ActionResult` returns no matches; file
|
|
78
|
+
deleted.
|
|
79
|
+
- [ ] `Provision::Windows#provision_commands` no longer returns
|
|
80
|
+
`['placeholder']`.
|
|
81
|
+
- [ ] `Utils.package_win_module` removed if confirmed unused.
|
|
82
|
+
- [ ] [`docs/ARCHITECTURE.md` §19](../ARCHITECTURE.md#19-open-questions--observed-dead-code)
|
|
83
|
+
items 1, 2, 3 deleted.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# RFC 0011: Make the provisioner factory honor configuration
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Author:** TBD
|
|
5
|
+
- **Created:** 2026-04-30
|
|
6
|
+
- **Priority:** Low
|
|
7
|
+
- **Category:** Refactor
|
|
8
|
+
- **Affected components:** `lib/cem_acpt/config/base.rb`, `lib/cem_acpt/provision.rb`
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`Config::Base#add_static_options!` force-sets `provisioner =
|
|
13
|
+
'terraform'` after every other config source has been merged. The
|
|
14
|
+
`Provision.new_provisioner` factory still branches on
|
|
15
|
+
`config.get('provisioner')` and raises for unknown values — so the
|
|
16
|
+
factory looks pluggable but is not. Either honor the config or remove
|
|
17
|
+
the factory.
|
|
18
|
+
|
|
19
|
+
## Background
|
|
20
|
+
|
|
21
|
+
[`docs/ARCHITECTURE.md` §7 / §19 (item 4)](../ARCHITECTURE.md#7-provisioner-terraform):
|
|
22
|
+
|
|
23
|
+
> `provisioner` is currently force-set to `'terraform'` in
|
|
24
|
+
> `Config::Base#add_static_options!`, so this dispatch is effectively
|
|
25
|
+
> fixed today.
|
|
26
|
+
|
|
27
|
+
`lib/cem_acpt/provision.rb` (factory) reads the key but never sees
|
|
28
|
+
anything other than `'terraform'` because of the static override.
|
|
29
|
+
|
|
30
|
+
## Problem
|
|
31
|
+
|
|
32
|
+
- The factory pattern signals an extensibility surface that does not
|
|
33
|
+
exist.
|
|
34
|
+
- A user who sets `provisioner: foo` in config gets silently
|
|
35
|
+
overridden — `-X` will show the override but the runtime behavior
|
|
36
|
+
is unchanged.
|
|
37
|
+
- This blocks experimentation with a Pulumi/Packer/local-only
|
|
38
|
+
provisioner without first untangling the static set.
|
|
39
|
+
|
|
40
|
+
## Proposal
|
|
41
|
+
|
|
42
|
+
Make the static layer set a *default* if absent, instead of
|
|
43
|
+
clobbering:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# Config::Base#add_static_options!
|
|
47
|
+
@config[:provisioner] ||= 'terraform'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
…and surface the relevant CLI flag in `Cli` (`--provisioner NAME`) so
|
|
51
|
+
users can opt into alternates. The `Provision.new_provisioner` factory
|
|
52
|
+
already raises a clear error for unknown values, which is the desired
|
|
53
|
+
behavior.
|
|
54
|
+
|
|
55
|
+
A complementary, even smaller change: if we don't actually intend to
|
|
56
|
+
support alternate provisioners, **remove the factory** and have
|
|
57
|
+
`TestRunner` instantiate `Provision::Terraform` directly. That is more
|
|
58
|
+
honest given today's code.
|
|
59
|
+
|
|
60
|
+
## Recommendation
|
|
61
|
+
|
|
62
|
+
Do the smaller change (remove the factory and the dispatch) for now.
|
|
63
|
+
Re-introduce the factory when there is a concrete second
|
|
64
|
+
implementation in flight. Premature extensibility cost us little here,
|
|
65
|
+
but it adds a layer of indirection that contributors have to chase.
|
|
66
|
+
|
|
67
|
+
## Alternatives Considered
|
|
68
|
+
|
|
69
|
+
- **Leave as-is.** Documentation drift continues; future contributors
|
|
70
|
+
keep mistaking the factory for an extension point.
|
|
71
|
+
- **Implement a second provisioner now.** Out of scope.
|
|
72
|
+
|
|
73
|
+
## Risks & Migration
|
|
74
|
+
|
|
75
|
+
- No user-visible change if the factory is removed; the only code path
|
|
76
|
+
through it today is `'terraform'`.
|
|
77
|
+
- Any out-of-tree consumer that referenced `Provision.new_provisioner`
|
|
78
|
+
directly (none in this repo) would need to update.
|
|
79
|
+
|
|
80
|
+
## Acceptance Criteria
|
|
81
|
+
|
|
82
|
+
- [ ] Decision recorded (factory kept-and-honored vs. factory
|
|
83
|
+
removed).
|
|
84
|
+
- [ ] Static-set in `Config::Base#add_static_options!` updated
|
|
85
|
+
accordingly.
|
|
86
|
+
- [ ] [`docs/ARCHITECTURE.md` §7](../ARCHITECTURE.md#7-provisioner-terraform)
|
|
87
|
+
and §19 updated.
|
|
88
|
+
- [ ] Spec coverage adjusted (the existing `terraform_cmd_spec.rb` is
|
|
89
|
+
unaffected).
|
data/docs/rfcs/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# cem_acpt RFCs
|
|
2
|
+
|
|
3
|
+
Design proposals and bug-fix plans for `cem_acpt`. Each RFC is a
|
|
4
|
+
self-contained Markdown file using the layout in
|
|
5
|
+
[`0000-template.md`](0000-template.md).
|
|
6
|
+
|
|
7
|
+
## Index
|
|
8
|
+
|
|
9
|
+
| # | Title | Priority | Category |
|
|
10
|
+
|------|------------------------------------------------------------------------------------|----------|-----------|
|
|
11
|
+
| 0001 | [Fix the bolt-missing skip path](0001-fix-bolt-missing-skip-path.md) | Critical | Bug |
|
|
12
|
+
| 0002 | [Fix malformed default `character_substitutions`](0002-fix-default-character-substitutions.md) | Critical | Bug |
|
|
13
|
+
| 0003 | [Ship a Windows image-builder Terraform template](0003-windows-image-builder-template.md) | Critical | Bug |
|
|
14
|
+
| 0004 | [Fix image-name truncation off-by-one](0004-image-name-truncation-off-by-one.md) | High | Bug |
|
|
15
|
+
| 0005 | [Replace `tests.first.include?('windows')` heuristic](0005-os-dispatch-replace-windows-heuristic.md) | High | Refactor |
|
|
16
|
+
| 0006 | [Make the Windows GCS bucket configurable](0006-configurable-windows-bucket.md) | High | Bug |
|
|
17
|
+
| 0007 | [Tighten `--quiet` semantics and fix logging typos](0007-logging-quiet-and-typos.md) | High | Bug |
|
|
18
|
+
| 0008 | [Namespace dynamically-created platform classes](0008-namespace-platform-classes.md) | Medium | Refactor |
|
|
19
|
+
| 0009 | [Wire up or remove orphaned Bolt log formatters](0009-bolt-log-formatter-cleanup.md) | Medium | Cleanup |
|
|
20
|
+
| 0010 | [Remove unreferenced top-level helpers and Windows placeholder](0010-dead-code-cleanup.md) | Medium | Cleanup |
|
|
21
|
+
| 0011 | [Make the provisioner factory honor configuration](0011-provisioner-factory-consistency.md) | Low | Refactor |
|
|
22
|
+
|
|
23
|
+
## Status
|
|
24
|
+
|
|
25
|
+
All RFCs above are **Proposed** unless otherwise marked in the
|
|
26
|
+
individual files. Authorship and review notes belong inside each RFC.
|
|
27
|
+
|
|
28
|
+
## Adding a new RFC
|
|
29
|
+
|
|
30
|
+
1. Copy [`0000-template.md`](0000-template.md) to the next available
|
|
31
|
+
number.
|
|
32
|
+
2. Fill in every section. Keep cross-references to
|
|
33
|
+
[`docs/ARCHITECTURE.md`](../ARCHITECTURE.md) accurate.
|
|
34
|
+
3. Append a row to the index above.
|
data/lib/cem_acpt/cli.rb
CHANGED
|
@@ -59,6 +59,12 @@ module CemAcpt
|
|
|
59
59
|
options[:bolt] ||= {}
|
|
60
60
|
options[:bolt][:keep_project] = true
|
|
61
61
|
end
|
|
62
|
+
|
|
63
|
+
opts.on('--windows-bucket BUCKET', 'GCS bucket name for the Windows test path. Example: --windows-bucket "my-win-bucket"') do |b|
|
|
64
|
+
options[:platform] ||= {}
|
|
65
|
+
options[:platform][:gcp] ||= {}
|
|
66
|
+
options[:platform][:gcp][:windows_bucket] = b
|
|
67
|
+
end
|
|
62
68
|
when :cem_acpt_image
|
|
63
69
|
opts.on('--dry-run', 'Logs the information for the images that would be created. Does not create images.') do
|
|
64
70
|
options[:dry_run] = true
|
|
@@ -126,7 +132,10 @@ module CemAcpt
|
|
|
126
132
|
options[:no_destroy_nodes] = true
|
|
127
133
|
end
|
|
128
134
|
|
|
129
|
-
opts.on(
|
|
135
|
+
opts.on(
|
|
136
|
+
'-q', '--quiet',
|
|
137
|
+
'Drop stdout (requires --log-file outside CI; CI mode keeps stdout for ::group:: support)',
|
|
138
|
+
) do
|
|
130
139
|
options[:quiet] = true
|
|
131
140
|
end
|
|
132
141
|
|
|
@@ -28,7 +28,7 @@ module CemAcpt
|
|
|
28
28
|
ci_mode: false,
|
|
29
29
|
config_file: nil,
|
|
30
30
|
image_name_builder: {
|
|
31
|
-
character_substitutions: ['_', '-'],
|
|
31
|
+
character_substitutions: [['_', '-']],
|
|
32
32
|
parts: ['cem-acpt', '$image_fam', '$collection', '$firewall'],
|
|
33
33
|
join_with: '-',
|
|
34
34
|
},
|
|
@@ -40,6 +40,9 @@ module CemAcpt
|
|
|
40
40
|
no_ephemeral_ssh_key: false,
|
|
41
41
|
platform: {
|
|
42
42
|
name: 'gcp',
|
|
43
|
+
gcp: {
|
|
44
|
+
windows_bucket: 'win_cem_acpt',
|
|
45
|
+
},
|
|
43
46
|
},
|
|
44
47
|
quiet: false,
|
|
45
48
|
test_data: {
|