Skip to content

refactor(cli): split cli.py into a commands package and share helpers - #384

Draft
digitalresistor wants to merge 5 commits into
google:mainfrom
digitalresistor:refactor/cli-commands-package
Draft

digitalresistor wants to merge 5 commits into
google:mainfrom
digitalresistor:refactor/cli-commands-package

Conversation

@digitalresistor

Copy link
Copy Markdown
Contributor

Description

This pull request splits cli/src/stellar_engine/cli.py into a commands/ package and moves the steps that the guided deploy reuses into library modules, with no change in behavior. It is the R13 refactor from the CLI tracking issue, and the feat(cli): guided deploy pull request builds on it.

  • The cli.py module keeps the Typer apps, the version callback, the exit code docstring and a registration table that fixes the stellar --help order.
  • The commands/init.py, commands/env.py, commands/imports.py, commands/render.py and commands/preflight.py modules hold one command each as plain functions and never import cli.
  • The commands/common.py module holds the shared options, the fail and warn helpers, the ExitOn context manager that replaces the repeated try and except blocks, the session and environment loaders, and the prompter, console and now factories that tests replace.
  • The render/pipeline.py module holds the render steps as functions that return warnings, summary rows and diff text instead of printing, so a later command can render a stage and read the result.
  • The render/writer.py module owns the MARKER line, is_generated and write_owned, which the import file and the Stage 0 phase file carried as duplicated code.
  • The config/answers.py module gains update, which changes an answers file through a callback, keeps its comments and key order, validates the result and skips the save when nothing changed.
  • The org_policies.py module wraps the organization policy scan and the save that stellar imports scan performs.
  • The preflight/checks.py module reports the pre-created Stage 0 resources through one precreate_status function and resolves role holders through one pure holder_member function instead of two copies of each.
  • The repo.py module reads locals, resources, modules and variables through four small HCL helpers instead of repeated traversals.
  • The tests gain the isolated_home, cli_runner, fake_cloud and fake_tools fixtures and the init_env, invoke and terraform_init helpers, and tests/test_cli.py splits into one module per command. Every test of the former module survives by name, and new tests cover ExitOn, load_env, answers.update, org_policies, the render pipeline, holder_member, and the exact env list and env show output.

Every command prints the same text, exits with the same codes and shows the same --help output as before. The cli/README.md file stays unchanged because no command, flag or output changes.

Part of #306. No separate issue exists for the split.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

This is a refactor with no behavior change, so none of the boxes applies.

Deployment & Compliance Impact

  • Applicable Regimes:
    • US Region Restricted (e.g., Access Policy constraint)
    • FedRAMP Moderate
    • FedRAMP High
    • DoD IL4
    • DoD IL5
    • General / All
  • NIST 800-53r5 Controls: None. The change touches the CLI code layout only.

Checklist

Code Quality & Reusability

  • My code adheres to the Maximize Reusability principle. I have not redefined common elements and have reused existing base configurations and modules where possible.
  • I have checked that no existing module or configuration in modules/ or fast/stages-aw/ can be leveraged for this change.
  • My code follows the established naming conventions outlined in docs/naming-convention.md.

Documentation

  • I have updated the README.md of the modified module or blueprint. The README needs no update because the commands, flags, outputs and exit codes stay the same.
  • I have added/updated documentation for inputs (variables) and outputs. Not applicable to the CLI.

Security

  • My change adheres to GCP security best practices and the principle of least privilege.
  • I have ensured compliance with the targeted regime (FedRAMP Moderate, FedRAMP High, IL4, IL5, etc.).

Testing

  • I have tested my changes locally.
  • I have included details of my testing in this PR.

Testing Performed

  • uv run pytest from the cli/ directory passes 962 tests, with the Terraform marked tests running against a local Terraform binary and all 14 syrupy snapshots matching.
  • uv run ruff format --check, uv run ruff check and uv run pyright from the cli/ directory report no findings.
  • python3 tools/check_boilerplate.py cli and pytest from the repository root pass.
  • Each of the five commits passes the same checks on its own.
  • The help output of stellar, stellar env, stellar imports and of the init, env list, env use, env show, imports scan, render and preflight commands was captured on main and on this branch under the same COLUMNS, TERM and NO_COLOR settings and compared byte for byte; every file is identical.
  • A scenario harness ran init, render, render --diff, render --force, env and imports scan on main and on this branch in temporary homes and compared stdout, stderr and exit codes; every scenario is identical.
  • The test names of the former tests/test_cli.py were compared with the split modules; all 66 survive.
  • No lab run, because the commands do not change.

preflight/checks.py reports the pre-created Stage 0 resources through
one precreate_status function and resolves role holders through a pure
holder_member function, and repo.py reads locals, resources, modules and
variables through four small HCL helpers instead of repeated traversals.
  * Every check keeps its results, messages and order, so preflight
    output does not change.
The render steps move from private helpers in cli.py into the
render/pipeline.py module, which returns warnings, rows and diff text
instead of printing, and render/writer.py now owns the generated-file
marker, is_generated and write_owned that the phase and import files
carried as duplicated code.
  * No command changes behavior; the next commits wire stellar render to
    the new module, which runs the same steps in the same order.
config/answers.py gains update, which changes an answers file through a
callback, keeps comments and key order, validates the result and skips
the save when nothing changed. The new org_policies.py module holds the
organization policy scan and save that stellar imports scan performs.
  * Commands do not change yet; a later commit wires stellar imports
    scan to the new module without changing its output.
cli.py keeps the Typer apps, the version callback and a registration
table, and one module per command lives under commands/, with the
shared options, exit codes, the ExitOn context manager, the environment
loaders and the prompter, console and now factories in
commands/common.py.
  * Every command prints the same text, exits with the same codes and
    shows the same --help output; the tests only change their monkeypatch
    targets.
conftest.py gains the isolated_home, cli_runner, fake_cloud and
fake_tools fixtures and helpers.py the init_env, invoke and
terraform_init helpers, so tests/test_cli.py splits into one module per
command and the new modules reuse the same setup.
  * Every test of the former module survives by name, and new tests
    cover ExitOn, load_env, the factories and the exact env list and env
    show output.
@github-actions github-actions Bot added the Scope - Fast Related to Fabric FAST deployment stages in the fast directory label Oct 29, 2026
@aghassemlouei aghassemlouei added Enhancement New feature or request Level of Effort - Medium Moderate task requiring thought and testing; typically takes a couple of days to a week Priority - Medium Standard features and non-blocking bugs; important for the current milestone but not urgent labels Oct 29, 2026
@digitalresistor digitalresistor mentioned this pull request Oct 30, 2026
12 of 19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement New feature or request Level of Effort - Medium Moderate task requiring thought and testing; typically takes a couple of days to a week Priority - Medium Standard features and non-blocking bugs; important for the current milestone but not urgent Scope - Fast Related to Fabric FAST deployment stages in the fast directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants