diff --git a/orchestrator/cli/commands/context.py b/orchestrator/cli/commands/context.py index 9516e13b4..b0453d4fd 100644 --- a/orchestrator/cli/commands/context.py +++ b/orchestrator/cli/commands/context.py @@ -16,7 +16,6 @@ from orchestrator.cli.utils.output.prints import ( ADO_NO_ACTIVE_CONTEXT_ERROR, console_print, - magenta, ) if typing.TYPE_CHECKING: @@ -39,20 +38,12 @@ def manage_contexts( See https://ibm.github.io/ado/getting-started/ado/#ado-context for detailed documentation and examples. - - Examples: - - # View the active context - ado context - - # Set local as your active context - ado context local """ ado_configuration: AdoConfiguration = ctx.obj @@ -70,12 +61,15 @@ def manage_contexts( def list_contexts( ctx: typer.Context, - simple: Annotated[ - bool, + output_format: Annotated[ + AdoGetSupportedOutputFormats, typer.Option( - "--simple", help="Display only context names.", show_default=False + "--output", + "-o", + help="Output format. Use 'name' to display only context names.", + show_default=False, ), - ] = False, + ] = AdoGetSupportedOutputFormats.DEFAULT, ) -> None: """ List available contexts. @@ -83,21 +77,19 @@ def list_contexts( See https://ibm.github.io/ado/getting-started/ado/#ado-context for detailed documentation and examples. - - Examples: - - # View available contexts and active context - ado contexts + # List available context names only + ado contexts -o name + # Get contexts as YAML + ado contexts -o yaml - # List available contexts - - ado contexts --simple + # Get contexts as JSON + ado contexts -o json """ ado_configuration: AdoConfiguration = ctx.obj @@ -116,7 +108,7 @@ def list_contexts( matching_space=None, minimize_output=True, no_trunc=False, - output_format=AdoGetSupportedOutputFormats.DEFAULT, + output_format=output_format, resource_id=None, resource_type=AdoGetSupportedResourceTypes.CONTEXT, show_deprecated=False, @@ -124,21 +116,14 @@ def list_contexts( ) # NOTE: there will always be at least one context (local) - get_context( - parameters=parameters, - simplify_output=simple, - ) + get_context(parameters=parameters) - if simple: - return - - if ado_configuration.active_context is None: + # Warn user if no context is active only when using the DEFAULT format + if ( + output_format == AdoGetSupportedOutputFormats.DEFAULT + and ado_configuration.active_context is None + ): console_print(ADO_NO_ACTIVE_CONTEXT_ERROR, stderr=True) - return - - console_print( - f"\nThe active context is: {magenta(ado_configuration.active_context)}" - ) def register_context_command(app: typer.Typer) -> None: @@ -151,5 +136,5 @@ def register_context_command(app: typer.Typer) -> None: def register_contexts_command(app: typer.Typer) -> None: app.command( name="contexts", - options_metavar="[--simple]", + options_metavar="[--output | -o ]", )(list_contexts) diff --git a/orchestrator/cli/resources/context/get.py b/orchestrator/cli/resources/context/get.py index 04d28b6f1..fa401b17f 100644 --- a/orchestrator/cli/resources/context/get.py +++ b/orchestrator/cli/resources/context/get.py @@ -9,10 +9,9 @@ from orchestrator.cli.models.parameters import AdoGetCommandParameters from orchestrator.cli.models.types import AdoGetSupportedOutputFormats from orchestrator.cli.utils.output.prints import ( - ADO_INFO_EMPTY_DATAFRAME, + ADO_NO_CONTEXT_AVAILABLE_ERROR, ERROR, HINT, - WARN, console_print, context_not_in_available_contexts_error_str, cyan, @@ -26,10 +25,9 @@ def get_context( parameters: AdoGetCommandParameters, - simplify_output: bool = False, ) -> None: - import rich.box + # Never truncate the CONTEXT (name) column if not parameters.no_trunc: parameters.no_trunc = ["CONTEXT"] @@ -39,11 +37,7 @@ def get_context( # The only possible way for this should be when the user is # providing a context with -c and the ado context dir is empty if len(available_contexts) == 0: - console_print( - f"{WARN}There are no contexts available.\n" - f"{HINT}You can create a context with {cyan('ado create context')}", - stderr=True, - ) + console_print(ADO_NO_CONTEXT_AVAILABLE_ERROR, stderr=True) raise typer.Exit(1) if parameters.resource_id: @@ -65,23 +59,18 @@ def get_context( parameters.exclude_default = False if parameters.output_format == AdoGetSupportedOutputFormats.NAME: - # NAME format: output only context identifiers, one per line + # NAME format: output only context names, one per line for context in sorted(available_contexts): console_print(context) return if parameters.output_format == AdoGetSupportedOutputFormats.DEFAULT: - if simplify_output: - _simple_contexts_formatting(contexts=available_contexts) - return + import rich.box contexts_df = _prepare_context_dataframe( contexts=available_contexts, - default_context=parameters.ado_configuration.active_context, + active_context=parameters.ado_configuration.active_context, ) - if contexts_df.empty: - console_print(ADO_INFO_EMPTY_DATAFRAME, stderr=True) - return console_print( dataframe_to_rich_table( @@ -98,19 +87,25 @@ def get_context( format_resource_for_ado_get_custom_format, ) + to_print = [] try: - to_print = [ - ProjectContext.model_validate( - yaml.safe_load( - parameters.ado_configuration.project_context_path_for_context( - ctx - ).read_text() + for ctx in available_contexts: + to_print.append( + ProjectContext.model_validate( + yaml.safe_load( + parameters.ado_configuration.project_context_path_for_context( + ctx + ).read_text() + ) ) ) - for ctx in available_contexts - ] except pydantic.ValidationError as e: - console_print(f"{ERROR}One of the contexts was not valid:\n{e}", stderr=True) + console_print( + f"{ERROR}Context {cyan(ctx)} was not valid:\n\n{e}\n\n" + f"{HINT}You can manually update the context file at: '{parameters.ado_configuration.project_context_path_for_context(ctx)}'\n" + f"\tAlternatively, delete the context with {cyan(f'ado delete context {ctx}')}", + stderr=True, + ) raise typer.Exit(1) from e # AP: it's more readable to write this than to @@ -125,20 +120,15 @@ def get_context( ) -def _simple_contexts_formatting(contexts: list[str]) -> None: - for context in sorted(contexts): - console_print(context) - - def _prepare_context_dataframe( - contexts: list[str], default_context: str | None + contexts: list[str], active_context: str | None ) -> "pd.DataFrame": import pandas as pd - default_context_column = [ - ":white_check_mark:" if ctx == default_context else "" for ctx in contexts + active_context_column = [ + ":white_check_mark:" if ctx == active_context else "" for ctx in contexts ] - output_df = pd.DataFrame({"CONTEXT": contexts, "DEFAULT": default_context_column}) + output_df = pd.DataFrame({"CONTEXT": contexts, "ACTIVE": active_context_column}) # Sort contexts by name output_df = output_df.sort_values(by=["CONTEXT"], axis="rows") diff --git a/orchestrator/cli/utils/output/prints.py b/orchestrator/cli/utils/output/prints.py index 45c053983..20e39caa9 100644 --- a/orchestrator/cli/utils/output/prints.py +++ b/orchestrator/cli/utils/output/prints.py @@ -38,6 +38,11 @@ "or activate one with [b cyan]ado context[/b cyan]" ) +ADO_NO_CONTEXT_AVAILABLE_ERROR = ( + f"{WARN}There are no contexts available.\n" + f"{HINT}You can create a context with [b cyan]ado create context[/b cyan]" +) + # Spinners ADO_SPINNER_CONNECTING_TO_DB = "Connecting to the database" ADO_SPINNER_QUERYING_DB = "Querying the database" diff --git a/tests/ado/context/test_ado_context.py b/tests/ado/context/test_ado_context.py index a2840fc77..2ae4db5df 100644 --- a/tests/ado/context/test_ado_context.py +++ b/tests/ado/context/test_ado_context.py @@ -254,33 +254,29 @@ def test_ado_contexts_list_contexts(tmp_path: Path) -> None: ) assert ado_contexts_default_output_result.exit_code == 0 ado_contexts_default_output_expected_output = ( - "┌───────┬────────────────┬─────────┐\n" - "│ INDEX │ CONTEXT │ DEFAULT │\n" - "├───────┼────────────────┼─────────┤\n" - "│ 0 │ first-context │ │\n" - "│ 1 │ local │ ✅ │\n" - "│ 2 │ second-context │ │\n" - "└───────┴────────────────┴─────────┘\n" - "\n" - "The active context is: local\n" + "┌───────┬────────────────┬────────┐\n" + "│ INDEX │ CONTEXT │ ACTIVE │\n" + "├───────┼────────────────┼────────┤\n" + "│ 0 │ first-context │ │\n" + "│ 1 │ local │ ✅ │\n" + "│ 2 │ second-context │ │\n" + "└───────┴────────────────┴────────┘\n" ) assert ( ado_contexts_default_output_result.output == ado_contexts_default_output_expected_output ) - # Test with the simple output - ado_contexts_simple_output_result = runner.invoke( + # Test with the name output format + ado_contexts_name_output_result = runner.invoke( ado, - ["--override-ado-app-dir", str(tmp_path), "contexts", "--simple"], - ) - assert ado_contexts_simple_output_result.exit_code == 0 - ado_contexts_simple_output_expected_output = ( - "first-context\nlocal\nsecond-context\n" + ["--override-ado-app-dir", str(tmp_path), "contexts", "-o", "name"], ) + assert ado_contexts_name_output_result.exit_code == 0 + ado_contexts_name_output_expected_output = "first-context\nlocal\nsecond-context\n" assert ( - ado_contexts_simple_output_result.output - == ado_contexts_simple_output_expected_output + ado_contexts_name_output_result.output + == ado_contexts_name_output_expected_output ) @@ -314,8 +310,8 @@ def test_ado_contexts_list_contexts_with_context_and_empty_dir_override( ) assert ado_contexts_result.output == ado_contexts_expected_output - # Test with the simple output - ado_contexts_simple_output_result = runner.invoke( + # Test with the name output format + ado_contexts_name_output_result = runner.invoke( ado, [ "--override-ado-app-dir", @@ -323,17 +319,18 @@ def test_ado_contexts_list_contexts_with_context_and_empty_dir_override( "-c", context_location, "contexts", - "--simple", + "-o", + "name", ], ) - assert ado_contexts_simple_output_result.exit_code == 1 - ado_contexts_simple_output_expected_output = ( + assert ado_contexts_name_output_result.exit_code == 1 + ado_contexts_name_output_expected_output = ( "WARN: There are no contexts available.\n" "HINT: You can create a context with ado create context\n" ) assert ( - ado_contexts_simple_output_result.output - == ado_contexts_simple_output_expected_output + ado_contexts_name_output_result.output + == ado_contexts_name_output_expected_output ) @@ -382,31 +379,35 @@ def test_ado_contexts_list_contexts_with_context_and_valid_dir_override( ) assert ado_contexts_default_output_result.exit_code == 0 ado_contexts_default_output_expected_output = ( - "┌───────┬────────────────┬─────────┐\n" - "│ INDEX │ CONTEXT │ DEFAULT │\n" - "├───────┼────────────────┼─────────┤\n" - "│ 0 │ first-context │ │\n" - "│ 1 │ local │ │\n" - "│ 2 │ second-context │ │\n" - "└───────┴────────────────┴─────────┘\n" - "\n" - f"The active context is: {valid_ado_project_context.project}\n" + "┌───────┬────────────────┬────────┐\n" + "│ INDEX │ CONTEXT │ ACTIVE │\n" + "├───────┼────────────────┼────────┤\n" + "│ 0 │ first-context │ │\n" + "│ 1 │ local │ │\n" + "│ 2 │ second-context │ │\n" + "└───────┴────────────────┴────────┘\n" ) assert ( ado_contexts_default_output_result.output == ado_contexts_default_output_expected_output ) - # Test with the simple output - ado_contexts_simple_output_result = runner.invoke( + # Test with the name output format + ado_contexts_name_output_result = runner.invoke( ado, - ["--override-ado-app-dir", str(tmp_path), "contexts", "--simple"], - ) - assert ado_contexts_simple_output_result.exit_code == 0 - ado_contexts_simple_output_expected_output = ( - "first-context\nlocal\nsecond-context\n" + [ + "--override-ado-app-dir", + str(tmp_path), + "-c", + context_location, + "contexts", + "-o", + "name", + ], ) + assert ado_contexts_name_output_result.exit_code == 0 + ado_contexts_name_output_expected_output = "first-context\nlocal\nsecond-context\n" assert ( - ado_contexts_simple_output_result.output - == ado_contexts_simple_output_expected_output + ado_contexts_name_output_result.output + == ado_contexts_name_output_expected_output ) diff --git a/website/docs/getting-started/ado.md b/website/docs/getting-started/ado.md index a66286d12..1304a5ce6 100644 --- a/website/docs/getting-started/ado.md +++ b/website/docs/getting-started/ado.md @@ -65,7 +65,19 @@ Similar to `oc projects`, users can list available contexts by running: ado contexts ``` -The default context will also be printed out. +It's also possible to output this information in multiple formats via the +`-o/--output` flag: + +```shell +# List context names only +ado contexts -o name + +# Export contexts as YAML +ado contexts -o yaml + +# Export contexts as JSON +ado contexts -o json +``` ##### Switching between contexts