Skip to content

Commit

Permalink
fix: add a table for package help text. (#5298)
Browse files Browse the repository at this point in the history
* fix: add a table for package help text.

* Update samcli/commands/package/core/command.py

Co-authored-by: Daniel Mil <[email protected]>

* tests: fix strings in package help text

* fix: PR comments

* fix: PR comments.

---------

Co-authored-by: Daniel Mil <[email protected]>
  • Loading branch information
sriram-mv and mildaniel authored Jun 28, 2023
1 parent 663c88d commit 9a71591
Show file tree
Hide file tree
Showing 12 changed files with 373 additions and 16 deletions.
6 changes: 6 additions & 0 deletions samcli/cli/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import List, Optional, cast

import click
from rich.console import Console

from samcli.cli.formatters import RootCommandHelpTextFormatter
from samcli.commands.exceptions import AWSServiceClientError
Expand Down Expand Up @@ -44,6 +45,11 @@ def __init__(self):
self._session_id = str(uuid.uuid4())
self._experimental = False
self._exception = None
self._console = Console()

@property
def console(self):
return self._console

@property
def exception(self):
Expand Down
8 changes: 4 additions & 4 deletions samcli/commands/_utils/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def no_progressbar_click_option():
default=False,
required=False,
is_flag=True,
help="Does not showcase a progress bar when uploading artifacts to s3 and pushing docker images to ECR",
help="Does not showcase a progress bar when uploading artifacts to S3 and pushing docker images to ECR",
)


Expand Down Expand Up @@ -679,9 +679,9 @@ def resolve_s3_click_option(guided):
required=False,
is_flag=True,
callback=callback,
help="Automatically resolve s3 bucket for non-guided deployments. "
"Enabling this option will also create a managed default s3 bucket for you. "
"If you do not provide a --s3-bucket value, the managed bucket will be used. "
help="Automatically resolve AWS S3 bucket for non-guided deployments. "
"Enabling this option will also create a managed default AWS S3 bucket for you. "
"If one does not provide a --s3-bucket value, the managed bucket will be used. "
"Do not use --guided with this option.",
)

Expand Down
35 changes: 23 additions & 12 deletions samcli/commands/package/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
template_click_option,
use_json_option,
)
from samcli.commands.package.core.command import PackageCommand
from samcli.lib.bootstrap.bootstrap import manage_stack
from samcli.lib.cli_validation.image_repository_validation import image_repository_validation
from samcli.lib.telemetry.metric import track_command, track_template_warnings
Expand All @@ -42,20 +43,30 @@ def resources_and_properties_help_string():
)


HELP_TEXT = (
"""The SAM package command creates and uploads artifacts based on the package type of a given resource.
It uploads local images to ECR for `Image` package types.
It creates zip of your code and dependencies and uploads it to S3 for other package types.
The command returns a copy of your template, replacing references to local artifacts
with the AWS location where the command uploaded the artifacts.
The following resources and their property locations are supported.
"""
+ resources_and_properties_help_string()
)
DESCRIPTION = """
Creates and uploads artifacts based on the package type of a given resource.
It uploads local images to ECR for `Image` package types.
It creates a zip of code and dependencies and uploads it to S3 for `Zip` package types.
A new template is returned which replaces references to local artifacts
with the AWS location where the command uploaded the artifacts.
"""


@click.command("package", short_help=SHORT_HELP, help=HELP_TEXT, context_settings=dict(max_content_width=120))
@click.command(
"package",
short_help=SHORT_HELP,
context_settings={
"ignore_unknown_options": False,
"allow_interspersed_args": True,
"allow_extra_args": True,
"max_content_width": 120,
},
cls=PackageCommand,
help=SHORT_HELP,
description=DESCRIPTION,
requires_credentials=True,
)
@configuration_option(provider=TomlProvider(section="parameters"))
@template_click_option(include_build=True)
@click.option(
Expand Down
Empty file.
138 changes: 138 additions & 0 deletions samcli/commands/package/core/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"""
`sam package` command class for help text visual layer.
"""
import click
from click import Context, style
from rich.table import Table

from samcli.cli.core.command import CoreCommand
from samcli.cli.row_modifiers import RowDefinition, ShowcaseRowModifier
from samcli.commands.package.core.formatters import PackageCommandHelpTextFormatter
from samcli.commands.package.core.options import OPTIONS_INFO
from samcli.lib.utils.resources import resources_generator

COL_SIZE_MODIFIER = 38


class PackageCommand(CoreCommand):
"""
`sam` package specific command class that specializes in the visual appearance
of `sam package` help text.
It hosts a custom formatter, examples, table for supported resources, acronyms
and how options are to be used in the CLI for `sam package`.
"""

class CustomFormatterContext(Context):
formatter_class = PackageCommandHelpTextFormatter

context_class = CustomFormatterContext

@staticmethod
def format_examples(ctx: Context, formatter: PackageCommandHelpTextFormatter):
with formatter.indented_section(name="Examples", extra_indents=1):
with formatter.indented_section(name="Automatic resolution of S3 buckets", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name=style(f"$ {ctx.command_path} --resolve-s3"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
],
col_max=COL_SIZE_MODIFIER,
)
with formatter.indented_section(name="Get packaged template", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name=style(f"$ {ctx.command_path} --resolve-s3 --output-template-file packaged.yaml"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
],
col_max=COL_SIZE_MODIFIER,
)
with formatter.indented_section(name="Customized location for uploading artifacts", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name=style(
f"$ {ctx.command_path} --s3-bucket S3_BUCKET --output-template-file packaged.yaml"
),
extra_row_modifiers=[ShowcaseRowModifier()],
),
],
col_max=COL_SIZE_MODIFIER,
)

@staticmethod
def format_table(formatter: PackageCommandHelpTextFormatter):
with formatter.section(name="Supported Resources"):
pass
ctx = click.get_current_context()
table = Table(width=ctx.max_content_width)
table.add_column("Resource")
table.add_column("Location")
for resource, location in resources_generator():
table.add_row(resource, location)
with ctx.obj.console.capture() as capture:
ctx.obj.console.print(table)
formatter.write_rd(
[
RowDefinition(name="\n"),
RowDefinition(name=capture.get()),
],
col_max=COL_SIZE_MODIFIER,
)

@staticmethod
def format_acronyms(formatter: PackageCommandHelpTextFormatter):
with formatter.indented_section(name="Acronyms", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name="S3",
text="Simple Storage Service",
extra_row_modifiers=[ShowcaseRowModifier()],
),
RowDefinition(
name="ECR",
text="Elastic Container Registry",
extra_row_modifiers=[ShowcaseRowModifier()],
),
RowDefinition(
name="KMS",
text="Key Management Service",
extra_row_modifiers=[ShowcaseRowModifier()],
),
],
col_max=COL_SIZE_MODIFIER,
)

def format_options(self, ctx: Context, formatter: PackageCommandHelpTextFormatter) -> None: # type:ignore
# `ignore` is put in place here for mypy even though it is the correct behavior,
# as the `formatter_class` can be set in subclass of Command. If ignore is not set,
# mypy raises argument needs to be HelpFormatter as super class defines it.

self.format_description(formatter)
PackageCommand.format_examples(ctx, formatter)
PackageCommand.format_table(formatter)
PackageCommand.format_acronyms(formatter)

CoreCommand._format_options(
ctx=ctx,
params=self.get_params(ctx),
formatter=formatter,
formatting_options=OPTIONS_INFO,
write_rd_overrides={"col_max": COL_SIZE_MODIFIER},
)
19 changes: 19 additions & 0 deletions samcli/commands/package/core/formatters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from samcli.cli.formatters import RootCommandHelpTextFormatter
from samcli.cli.row_modifiers import BaseLineRowModifier
from samcli.commands.deploy.core.options import ALL_OPTIONS


class PackageCommandHelpTextFormatter(RootCommandHelpTextFormatter):
# Picked an additive constant that gives an aesthetically pleasing look.
ADDITIVE_JUSTIFICATION = 15

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Add Additional space after determining the longest option.
# However, do not justify with padding for more than half the width of
# the terminal to retain aesthetics.
self.left_justification_length = min(
max([len(option) for option in ALL_OPTIONS]) + self.ADDITIVE_JUSTIFICATION,
self.width // 2 - self.indent_increment,
)
self.modifiers = [BaseLineRowModifier()]
68 changes: 68 additions & 0 deletions samcli/commands/package/core/options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""
Package Command Options related Datastructures for formatting.
"""
from typing import Dict, List

from samcli.cli.core.options import ALL_COMMON_OPTIONS, add_common_options_info
from samcli.cli.row_modifiers import RowDefinition

# The ordering of the option lists matter, they are the order in which options will be displayed.

REQUIRED_OPTIONS: List[str] = ["s3_bucket", "resolve_s3"]

AWS_CREDENTIAL_OPTION_NAMES: List[str] = ["region", "profile"]

INFRASTRUCTURE_OPTION_NAMES: List[str] = [
"s3_prefix",
"image_repository",
"image_repositories",
"kms_key_id",
"metadata",
]

DEPLOYMENT_OPTIONS: List[str] = [
"force_upload",
]

CONFIGURATION_OPTION_NAMES: List[str] = ["config_env", "config_file"]

ADDITIONAL_OPTIONS: List[str] = [
"no_progressbar",
"signing_profiles",
"template_file",
"output_template_file",
"use_json",
]

ALL_OPTIONS: List[str] = (
REQUIRED_OPTIONS
+ AWS_CREDENTIAL_OPTION_NAMES
+ INFRASTRUCTURE_OPTION_NAMES
+ DEPLOYMENT_OPTIONS
+ CONFIGURATION_OPTION_NAMES
+ ADDITIONAL_OPTIONS
+ ALL_COMMON_OPTIONS
)

OPTIONS_INFO: Dict[str, Dict] = {
"Required Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(REQUIRED_OPTIONS)}},
"AWS Credential Options": {
"option_names": {opt: {"rank": idx} for idx, opt in enumerate(AWS_CREDENTIAL_OPTION_NAMES)}
},
"Infrastructure Options": {
"option_names": {opt: {"rank": idx} for idx, opt in enumerate(INFRASTRUCTURE_OPTION_NAMES)}
},
"Package Management Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(DEPLOYMENT_OPTIONS)}},
"Configuration Options": {
"option_names": {opt: {"rank": idx} for idx, opt in enumerate(CONFIGURATION_OPTION_NAMES)},
"extras": [
RowDefinition(name="Learn more about configuration files at:"),
RowDefinition(
name="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli"
"-config.html. "
),
],
},
"Additional Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(ADDITIONAL_OPTIONS)}},
}
add_common_options_info(OPTIONS_INFO)
6 changes: 6 additions & 0 deletions tests/unit/cli/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from unittest import TestCase
from unittest.mock import patch, ANY

from rich.console import Console

from samcli.cli.context import Context
from samcli.lib.utils.sam_logging import (
SamCliLogger,
Expand All @@ -20,6 +22,10 @@ def test_must_initialize_with_defaults(self):

self.assertEqual(ctx.debug, False, "debug must default to False")

def test_must_have_console(self):
ctx = Context()
self.assertTrue(isinstance(ctx.console, Console))

def test_must_set_get_debug_flag(self):
ctx = Context()

Expand Down
Empty file.
Loading

0 comments on commit 9a71591

Please sign in to comment.