Implementation Plan: CascadeGuard CLI Improvements
Overview
Implement config inheritance via .cascadeguard.yaml defaults, update validation to use merged defaults, add images generate and ci generate CLI commands, and update the open-secure-images config file. All changes are in Python, targeting repos/cascadeguard/app/app.py, repos/cascadeguard/app/generate_ci.py, and repos/cascadeguard-open-secure-images/.cascadeguard.yaml.
Tasks
-
1. Implement
merge_defaults()and updateload_config()in app.py-
1.1 Add
merge_defaults(images, config)function toapp.py- Implement shallow merge of
defaults.registry,defaults.repository, anddefaults.local.dirfrom config onto each image dict - Return a new list without mutating the original image list or its dicts
- If config has no
defaultssection, return shallow copies of originals unchanged - Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9
- Implement shallow merge of
-
1.2 Update
load_config()inapp.pyto handle malformed YAML- Add error handling for YAML parse errors (print to stderr, return exit code 1)
- Add check that root value is a dict (print to stderr, return exit code 1)
- Silently ignore unknown keys for forward compatibility
- Requirements: 1.1, 1.2, 1.3, 1.4, 1.5
-
[ ]* 1.3 Write property tests for
merge_defaults()- Property 1: Default inheritance — if image lacks a key and config defaults provide it, merged image has the default value
- Validates: Requirements 2.1, 2.2, 2.3
- Property 2: Override precedence — if image already has a key, merged image retains the image’s original value
- Validates: Requirements 2.4, 2.5, 2.6
- Property 3: Non-mutation — calling merge_defaults does not modify the original image list or its dicts
- Validates: Requirement 2.7
- Property 4: Length preservation — merge_defaults returns a list of the same length as input
- Validates: Requirement 2.8
- Property 5: No-defaults backward compatibility — with no defaults section, returns copies equivalent to originals
- Validates: Requirements 2.9, 7.1
-
-
2. Update
cmd_validate()to use config inheritance-
2.1 Modify
cmd_validate()inapp.pyto load config and merge defaults before validation- Call
load_config()withimages_yaml.parentas repo root - Call
merge_defaults()on loaded images before checking required fields - For enabled images: require
name,registry, anddockerfile(notrepository) - For disabled images (
enabled: false): require onlyname - Include hint in error messages that missing fields can be set in
.cascadeguard.yamldefaults - Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8
- Call
-
[ ]* 2.2 Write property tests for validation correctness
- Property 6: Disabled image leniency — disabled images with
namealways pass validation regardless of other missing fields - Validates: Requirement 3.4
- Property 7: Missing name always fails — any image without
namefails validation - Validates: Requirement 3.5
- Property 8: Validation correctness for enabled images — enabled images pass iff they have
name,registry, anddockerfileafter merge - Validates: Requirements 3.2, 3.3, 3.6, 3.7
- Property 6: Disabled image leniency — disabled images with
-
-
3. Checkpoint - Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
-
4. Add
images generateCLI command and handler-
4.1 Register
generatesubcommand underimagesinbuild_parser()- Add
--output-dir(default.),--cache-dir(default None) flags - The
--images-yamlflag is already on theimagesparent parser - Requirements: 6.1, 6.3, 6.5
- Add
-
4.2 Implement
cmd_images_generate(args)handler inapp.py- Import
generate_state.load_images_yaml,generate_state.generate_state_for_image,generate_state._generate_build_workflow - Load config, merge defaults, iterate images, call generate functions
- Return 0 on success, 1 on failure; print summary of generated files
- Requirements: 4.1, 4.2, 4.3, 4.4, 4.5
- Import
-
4.3 Wire
cmd_images_generateinto thecmd_imagesdispatcher- Add
"generate": cmd_images_generateto the dispatch dict incmd_images() - Requirements: 4.1, 6.1
- Add
-
[ ]* 4.4 Write unit tests for
images generatecommand- Test argument parsing for
images generate --output-dir /tmp --cache-dir /tmp/cache - Test handler with mocked
generate_statefunctions - Requirements: 4.1, 4.2, 4.3, 4.4, 4.5
- Test argument parsing for
-
-
5. Add
ci generateCLI command and handler-
5.1 Register
citop-level command withgeneratesubcommand inbuild_parser()- Add
--images-yaml(defaultimages.yaml),--output-dir(default.),--platform(default None),--dry-runflags - Requirements: 6.2, 6.4, 6.6
- Add
-
5.2 Implement
cmd_ci_generate(args)handler inapp.py- Import
generate_ci.generate_ci - Load config, merge defaults, call
generate_ci()with resolved platform - Return 0 on success, 1 on failure
- Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8
- Import
-
5.3 Add
cmd_cidispatcher and wire intomain()commands dict- Add
"ci": cmd_cito the top-level commands dict inmain() - Requirements: 5.1, 6.2
- Add
-
[ ]* 5.4 Write unit tests for
ci generatecommand- Test argument parsing for
ci generate --platform github --dry-run - Test handler with mocked
generate_cimodule - Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8
- Test argument parsing for
-
-
6. Checkpoint - Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
-
7. Update
.cascadeguard.yamlin open-secure-images and updategenerate_ci.py-
7.1 Add
defaultssection torepos/cascadeguard-open-secure-images/.cascadeguard.yaml- Add
defaults.registry: ghcr.io/cascadeguardanddefaults.local.dir: images - Preserve existing
ci: platform: githubsection - Requirements: 1.1, 2.1, 2.3
- Add
-
7.2 Update
generate_ci.pyto use merged defaults- Import and call
merge_defaults(or inline equivalent) so thatgenerate_ciresolves image fields from config defaults before generating workflows - Ensure images without explicit
registryget the default from.cascadeguard.yaml - Requirements: 5.1, 7.1, 7.2
- Import and call
-
[ ]* 7.3 Write unit tests verifying backward compatibility
- Test that CLI behaves identically when no
defaultssection exists in config - Test that existing commands are unaffected by the new code
- Requirements: 7.1, 7.2, 7.3
- Test that CLI behaves identically when no
-
-
8. Final checkpoint - Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
Notes
- Tasks marked with
*are optional and can be skipped for faster MVP - Each task references specific requirements for traceability
- Checkpoints ensure incremental validation
- Property tests validate universal correctness properties from the design document (Properties 1-8)
- The design uses Python throughout, so all implementation is in Python
generate_state.pyandgenerate_ci.pyare imported as modules by the new CLI handlers — no duplication of logic