Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "feat: Sam init interactive flow redesign (#3235)" #3281

Merged
merged 1 commit into from Sep 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 0 additions & 6 deletions samcli/commands/exceptions.py
Expand Up @@ -78,9 +78,3 @@ class AppPipelineTemplateMetadataException(UserException):
"""
Exception class when SAM is not able to parse the "metadata.json" file located in the SAM pipeline templates
"""


class InvalidInitOptionException(UserException):
"""
Exception class when user provides wrong options
"""
8 changes: 4 additions & 4 deletions samcli/commands/init/__init__.py
Expand Up @@ -13,7 +13,7 @@
from samcli.lib.utils.version_checker import check_newer_version
from samcli.local.common.runtime_template import RUNTIMES, SUPPORTED_DEP_MANAGERS, LAMBDA_IMAGES_RUNTIMES
from samcli.lib.telemetry.metric import track_command
from samcli.commands.init.interactive_init_flow import _get_runtime_from_image, get_sorted_runtimes
from samcli.commands.init.interactive_init_flow import _get_runtime_from_image
from samcli.commands.local.cli_common.click_mutex import Mutex
from samcli.lib.utils.packagetype import IMAGE, ZIP

Expand Down Expand Up @@ -142,7 +142,7 @@ def wrapped(*args, **kwargs):
@click.option(
"-r",
"--runtime",
type=click.Choice(get_sorted_runtimes(RUNTIMES)),
type=click.Choice(RUNTIMES),
help="Lambda Runtime of your app",
cls=Mutex,
not_required=["location", "base_image"],
Expand Down Expand Up @@ -273,9 +273,9 @@ def do_cli(
image_bool = name and pt_explicit and base_image
if location or zip_bool or image_bool:
# need to turn app_template into a location before we generate
templates = InitTemplates()
templates = InitTemplates(no_interactive)
if package_type == IMAGE and image_bool:
runtime = _get_runtime_from_image(base_image)
base_image, runtime = _get_runtime_from_image(base_image)
options = templates.init_options(package_type, runtime, base_image, dependency_manager)
if not app_template:
if len(options) == 1:
Expand Down
162 changes: 62 additions & 100 deletions samcli/commands/init/init_templates.py
Expand Up @@ -2,23 +2,20 @@
Manages the set of application templates.
"""

import re
import itertools
import json
import logging
import os
from pathlib import Path
from typing import Dict

import click

from samcli.cli.main import global_cfg
from samcli.commands.exceptions import UserException, AppTemplateUpdateException
from samcli.lib.utils.git_repo import GitRepo, CloneRepoException, CloneRepoUnstableStateException
from samcli.lib.utils.packagetype import IMAGE
from samcli.local.common.runtime_template import (
RUNTIME_DEP_TEMPLATE_MAPPING,
get_local_lambda_images_location,
)
from samcli.local.common.runtime_template import RUNTIME_DEP_TEMPLATE_MAPPING, get_local_lambda_images_location

LOG = logging.getLogger(__name__)
APP_TEMPLATES_REPO_URL = "https://github.com/aws/aws-sam-cli-app-templates"
Expand All @@ -30,9 +27,62 @@ class InvalidInitTemplateError(UserException):


class InitTemplates:
def __init__(self):
def __init__(self, no_interactive=False):
self._no_interactive = no_interactive
self._git_repo: GitRepo = GitRepo(url=APP_TEMPLATES_REPO_URL)
self.manifest_file_name = "manifest.json"

def prompt_for_location(self, package_type, runtime, base_image, dependency_manager):
"""
Prompt for template location based on other information provided in previous steps.

Parameters
----------
package_type : str
the package type, 'Zip' or 'Image', see samcli/lib/utils/packagetype.py
runtime : str
the runtime string
base_image : str
the base image string
dependency_manager : str
the dependency manager string

Returns
-------
location : str
The location of the template
app_template : str
The name of the template
"""
options = self.init_options(package_type, runtime, base_image, dependency_manager)

if len(options) == 1:
template_md = options[0]
else:
choices = list(map(str, range(1, len(options) + 1)))
choice_num = 1
click.echo("\nAWS quick start application templates:")
for o in options:
if o.get("displayName") is not None:
msg = "\t" + str(choice_num) + " - " + o.get("displayName")
click.echo(msg)
else:
msg = (
"\t"
+ str(choice_num)
+ " - Default Template for runtime "
+ runtime
+ " with dependency manager "
+ dependency_manager
)
click.echo(msg)
choice_num = choice_num + 1
choice = click.prompt("Template selection", type=click.Choice(choices), show_choices=False)
template_md = options[int(choice) - 1] # zero index
if template_md.get("init_location") is not None:
return (template_md["init_location"], template_md["appTemplate"])
if template_md.get("directory") is not None:
return os.path.join(self._git_repo.local_path, template_md["directory"]), template_md["appTemplate"]
raise InvalidInitTemplateError("Invalid template. This should not be possible, please raise an issue.")

def location_from_app_template(self, package_type, runtime, base_image, dependency_manager, app_template):
options = self.init_options(package_type, runtime, base_image, dependency_manager)
Expand All @@ -54,26 +104,23 @@ def _check_app_template(entry: Dict, app_template: str) -> bool:
return bool(entry["appTemplate"] == app_template)

def init_options(self, package_type, runtime, base_image, dependency_manager):
self.clone_templates_repo()
if self._git_repo.local_path is None:
return self._init_options_from_bundle(package_type, runtime, dependency_manager)
return self._init_options_from_manifest(package_type, runtime, base_image, dependency_manager)

def clone_templates_repo(self):
if not self._git_repo.clone_attempted:
shared_dir: Path = global_cfg.config_dir
try:
self._git_repo.clone(clone_dir=shared_dir, clone_name=APP_TEMPLATES_REPO_NAME, replace_existing=True)
except CloneRepoUnstableStateException as ex:
raise AppTemplateUpdateException(str(ex)) from ex
except (OSError, CloneRepoException):
LOG.debug("Clone error, attempting to use an old clone from a previous run")
# If can't clone, try using an old clone from a previous run if already exist
expected_previous_clone_local_path: Path = shared_dir.joinpath(APP_TEMPLATES_REPO_NAME)
if expected_previous_clone_local_path.exists():
self._git_repo.local_path = expected_previous_clone_local_path
if self._git_repo.local_path is None:
return self._init_options_from_bundle(package_type, runtime, dependency_manager)
return self._init_options_from_manifest(package_type, runtime, base_image, dependency_manager)

def _init_options_from_manifest(self, package_type, runtime, base_image, dependency_manager):
manifest_path = self.get_manifest_path()
manifest_path = os.path.join(self._git_repo.local_path, "manifest.json")
with open(str(manifest_path)) as fp:
body = fp.read()
manifest_body = json.loads(body)
Expand Down Expand Up @@ -124,88 +171,3 @@ def is_dynamic_schemas_template(self, package_type, app_template, runtime, base_
if option.get("appTemplate") == app_template:
return option.get("isDynamicTemplate", False)
return False

def get_hello_world_image_template(self, package_type, runtime, base_image, dependency_manager):
self.clone_templates_repo()
templates = self.init_options(package_type, runtime, base_image, dependency_manager)
template_display_name = "hello world"
hello_world_template = filter(lambda x: template_display_name in x["displayName"].lower(), list(templates))
return list(hello_world_template)

def get_app_template_location(self, template_directory):
return os.path.normpath(os.path.join(self._git_repo.local_path, template_directory))

def get_manifest_path(self):
return Path(self._git_repo.local_path, self.manifest_file_name)

def get_preprocessed_manifest(self, filter_value=None):
"""
This method get the manifest cloned from the git repo and preprocessed it.
Below is the link to manifest:
https://github.com/aws/aws-sam-cli-app-templates/blob/master/manifest.json

The structure of the manifest is shown below:
{
"dotnetcore2.1": [
{
"directory": "dotnetcore2.1/cookiecutter-aws-sam-hello-dotnet",
"displayName": "Hello World Example",
"dependencyManager": "cli-package",
"appTemplate": "hello-world",
"packageType": "Zip",
"useCaseName": "Serverless API"
},
]
}

Parameters
----------
filter_value : string, optional
This could be a runtime or a base-image, by default None

Returns
-------
[dict]
This is preprocessed manifest with the use_case as key
"""
self.clone_templates_repo()
manifest_path = self.get_manifest_path()
with open(str(manifest_path)) as fp:
body = fp.read()
manifest_body = json.loads(body)

preprocessed_manifest = {}
for template_runtime in manifest_body:
if filter_value and filter_value != template_runtime:
continue

template_list = manifest_body[template_runtime]
for template in template_list:
package_type = get_template_value("packageType", template)
use_case_name = get_template_value("useCaseName", template)
runtime = get_runtime(package_type, template_runtime)

use_case = preprocessed_manifest.get(use_case_name, {})
use_case[runtime] = use_case.get(runtime, {})
use_case[runtime][package_type] = use_case[runtime].get(package_type, [])
use_case[runtime][package_type].append(template)

preprocessed_manifest[use_case_name] = use_case
return preprocessed_manifest

def get_bundle_option(self, package_type, runtime, dependency_manager):
return self._init_options_from_bundle(package_type, runtime, dependency_manager)


def get_template_value(value, template):
if value not in template:
raise InvalidInitTemplateError(
f"Template is missing the value for {value} in manifest file. Please raise a a github issue."
)
return template.get(value)


def get_runtime(package_type, template_runtime):
if package_type == IMAGE:
template_runtime = re.split("/|-", template_runtime)[1]
return template_runtime