-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: SAM Init interactive flow redesign #3410
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
Merged
Merged
Changes from 9 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
61eba16
SAM INIT Update
jonife 4388d47
resolved conflict
jonife 9a8058f
test added and updated
jonife c0927f7
block default prompt when Image packageType is provide and skip use c…
jonife 5a0492a
update default hello world prompt
jonife 05aecef
Merge branch 'develop' into sam_init_revamp
mndeveci b56e08a
filtering updates
jonife fff8584
added typing
jonife e088c5d
Merge branch 'develop' into sam_init_revamp
mndeveci 0f65d73
Fixing failed canaries (#3532)
qingchm d6a9e70
(fix) stop resolving relative paths for Function image URI (#3531)
moelasmar 5059f37
update template_does_not_meet_filter_criteria use <dict>.get
jonife ad55a93
Merge branch 'develop' into sam_init_revamp
jonife File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,17 +1,15 @@ | ||||||||||||||
| """ | ||||||||||||||
| Manages the set of application templates. | ||||||||||||||
| """ | ||||||||||||||
|
|
||||||||||||||
| import re | ||||||||||||||
| import itertools | ||||||||||||||
| import json | ||||||||||||||
| import logging | ||||||||||||||
| import os | ||||||||||||||
| from pathlib import Path | ||||||||||||||
| from typing import Dict | ||||||||||||||
| from typing import Dict, Optional | ||||||||||||||
|
|
||||||||||||||
| import click | ||||||||||||||
| from samcli.cli.global_config import GlobalConfig | ||||||||||||||
|
|
||||||||||||||
| from samcli.commands.exceptions import UserException, AppTemplateUpdateException | ||||||||||||||
| from samcli.lib.utils.git_repo import GitRepo, CloneRepoException, CloneRepoUnstableStateException | ||||||||||||||
| from samcli.lib.utils.packagetype import IMAGE | ||||||||||||||
|
|
@@ -27,62 +25,9 @@ class InvalidInitTemplateError(UserException): | |||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class InitTemplates: | ||||||||||||||
| def __init__(self, no_interactive=False): | ||||||||||||||
| self._no_interactive = no_interactive | ||||||||||||||
| def __init__(self): | ||||||||||||||
| self._git_repo: GitRepo = GitRepo(url=APP_TEMPLATES_REPO_URL) | ||||||||||||||
|
|
||||||||||||||
| 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.") | ||||||||||||||
| self.manifest_file_name = "manifest.json" | ||||||||||||||
|
|
||||||||||||||
| 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) | ||||||||||||||
|
|
@@ -104,23 +49,26 @@ 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 = GlobalConfig().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): | ||||||||||||||
| # If can't clone, try using an old clone from a previous run if already exist | ||||||||||||||
| LOG.debug("Clone error, attempting to use an old clone from a previous run") | ||||||||||||||
| 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 = os.path.join(self._git_repo.local_path, "manifest.json") | ||||||||||||||
| manifest_path = self.get_manifest_path() | ||||||||||||||
| with open(str(manifest_path)) as fp: | ||||||||||||||
| body = fp.read() | ||||||||||||||
| manifest_body = json.loads(body) | ||||||||||||||
|
|
@@ -171,3 +119,126 @@ 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_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: Optional[str] = None, | ||||||||||||||
| app_template: Optional[str] = None, | ||||||||||||||
| package_type: Optional[str] = None, | ||||||||||||||
| dependency_manager: Optional[str] = None, | ||||||||||||||
| ) -> dict: | ||||||||||||||
| """ | ||||||||||||||
| 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 | ||||||||||||||
| app_template : string, optional | ||||||||||||||
| Application template generated | ||||||||||||||
| package_type : string, optional | ||||||||||||||
| The package type, 'Zip' or 'Image', see samcli/lib/utils/packagetype.py | ||||||||||||||
| dependency_manager : string, optional | ||||||||||||||
| dependency manager | ||||||||||||||
| 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) | ||||||||||||||
|
|
||||||||||||||
| # This would ensure the Use-Case Hello World Example appears | ||||||||||||||
| # at the top of list template example displayed to the Customer. | ||||||||||||||
| preprocessed_manifest = {"Hello World Example": {}} # type: dict | ||||||||||||||
| 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: | ||||||||||||||
| template_package_type = get_template_value("packageType", template) | ||||||||||||||
| use_case_name = get_template_value("useCaseName", template) | ||||||||||||||
mndeveci marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
| if not (template_package_type or use_case_name) or template_does_not_meet_filter_criteria( | ||||||||||||||
| app_template, package_type, dependency_manager, template | ||||||||||||||
| ): | ||||||||||||||
| continue | ||||||||||||||
| runtime = get_runtime(template_package_type, template_runtime) | ||||||||||||||
| use_case = preprocessed_manifest.get(use_case_name, {}) | ||||||||||||||
| use_case[runtime] = use_case.get(runtime, {}) | ||||||||||||||
| use_case[runtime][template_package_type] = use_case[runtime].get(template_package_type, []) | ||||||||||||||
| use_case[runtime][template_package_type].append(template) | ||||||||||||||
|
|
||||||||||||||
| preprocessed_manifest[use_case_name] = use_case | ||||||||||||||
|
|
||||||||||||||
| if not bool(preprocessed_manifest["Hello World Example"]): | ||||||||||||||
| del preprocessed_manifest["Hello World Example"] | ||||||||||||||
|
|
||||||||||||||
| 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: str, template: dict) -> Optional[str]: | ||||||||||||||
| if value not in template: | ||||||||||||||
| LOG.debug( | ||||||||||||||
| f"Template is missing the value for {value} in manifest file. Please raise a github issue." | ||||||||||||||
| + f" Template details: {template}" | ||||||||||||||
| ) | ||||||||||||||
| return template.get(value) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def get_runtime(package_type: Optional[str], template_runtime: str) -> str: | ||||||||||||||
| if package_type == IMAGE: | ||||||||||||||
| template_runtime = re.split("/|-", template_runtime)[1] | ||||||||||||||
| return template_runtime | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def template_does_not_meet_filter_criteria( | ||||||||||||||
| app_template: Optional[str], package_type: Optional[str], dependency_manager: Optional[str], template: dict | ||||||||||||||
| ) -> bool: | ||||||||||||||
| """ | ||||||||||||||
| Parameters | ||||||||||||||
| ---------- | ||||||||||||||
| app_template : Optional[str] | ||||||||||||||
| Application template generated | ||||||||||||||
| package_type : Optional[str] | ||||||||||||||
| The package type, 'Zip' or 'Image', see samcli/lib/utils/packagetype.py | ||||||||||||||
| dependency_manager : Optional[str] | ||||||||||||||
| Dependency manager | ||||||||||||||
| template : dict | ||||||||||||||
| key-value pair app template configuration | ||||||||||||||
|
|
||||||||||||||
| Returns | ||||||||||||||
| ------- | ||||||||||||||
| bool | ||||||||||||||
| True if template does not meet filter criteria else False | ||||||||||||||
| """ | ||||||||||||||
| return bool( | ||||||||||||||
| (app_template and app_template != template["appTemplate"]) | ||||||||||||||
| or (package_type and package_type != template["packageType"]) | ||||||||||||||
| or (dependency_manager and dependency_manager != template["dependencyManager"]) | ||||||||||||||
|
||||||||||||||
| (app_template and app_template != template["appTemplate"]) | |
| or (package_type and package_type != template["packageType"]) | |
| or (dependency_manager and dependency_manager != template["dependencyManager"]) | |
| (app_template and app_template != template.get("appTemplate")) | |
| or (package_type and package_type != template.get("packageType")) | |
| or (dependency_manager and dependency_manager != template.get("dependencyManager")) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.