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

Interactive Init #1454

Merged
merged 42 commits into from
Oct 29, 2019
Merged

Interactive Init #1454

merged 42 commits into from
Oct 29, 2019

Conversation

awood45
Copy link
Member

@awood45 awood45 commented Oct 9, 2019

This is not quite ready to merge yet - I have tested code paths manually, but there is an expansion of unit and integration testing coverage required to provide a sufficient regression suite.

However, the code proper should be ready to review in tandem with these efforts.

IMPORTANT NOTE: As implemented, this depends on the Python 2.7 deprecation being complete.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

This is populating new commands and changes some of the relevant
documentation. This is very much a WIP and not ready to merge.
This actually covers all values we need to prompt for, but is in dire
need of a refactoring, and the GitHub clone logic needs to be
implemented.
The flow works, most edge cases covered. Still have some development
features that need to be pulled out/resolved.
Handles most cases, still need to clean out some debug logic and write
full test suite.
More tests needed to meet the coverage bar since there's a lot of new
code, but the existing tests work.
With dependency-manager required, cannot have go showing a None type for
that field.
Primarily, refactoring the different primary interactive threads into
different methods.
@awood45 awood45 requested a review from sriram-mv October 9, 2019 19:22
Copy link
Contributor

@sriram-mv sriram-mv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do a more detailed pass.

auto_clone=True,
):
# check for mutually exclusive parameters
if location and app_template:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can potentially lift exception cases into decorators on do_cli

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That turned out to be a bit problematic when I tried it, as we don't always move to failure - rather, in some cases where we have missing required parameters we move to interactive, in other cases we fail. It's difficult to lift a partial of this out without making the code look more confusing to me.

expected_path = os.path.normpath(os.path.join(shared_dir, self._repo_name))
if self._should_clone_repo(expected_path):
try:
subprocess.check_output(["git", "clone", self._repo_url], cwd=shared_dir, stderr=subprocess.STDOUT)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on windows you might need to look for git.exe or git.cmd, Not sure which.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout, adding an investigation item on this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a modified approach based on what we do for AWS CLI. On the one hand, we may want to abstract that to a utility, on the other hand, AWS CLI dependency is going away anyways.

error_msg = """
ERROR: Missing required parameters, with --no-interactive set.

Must provide one of the following required parameter combinations:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not clear on what constitues a combination, I would put valid combinations in a list, so that its easy to grok for the user.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are only two, what format did you suggest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[--name--runtime , --dependency-manager, --app-template] or [--location]. Just having all the ands together in --name and --runtime and --dependency-manager and --app-template made it difficult for me to spot the combination.

options = self.init_options(runtime, dependency_manager)
choices = map(str, range(1, len(options) + 1))
choice_num = 1
for o in options:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for option in options, instead of o

if self._no_interactive:
return self._auto_clone
do_clone = click.confirm(
"This process will clone app templates from https://github.com/awslabs/aws-sam-cli-app-templates - is this ok?"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we prompt even in a non interactive mode here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I misread the indentation.



def do_interactive(location, runtime, dependency_manager, output_dir, name, app_template, no_input):
if location:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we convert if elses into: dictionary dispatch tables? https://medium.com/better-programming/dispatch-tables-in-python-d37bcc443b0b

Windows compatibility check, essentially, and this commit also includes
a Black reformatting.
This enables us to properly test prompts and improves code
coverage. We also managed to entirely remove a code path (location
cannot currently be missing when going to interactive).

Remaining work is to improve unit test coverage of init templates.
It was incorrect to use 'takewhile', as it terminates as soon as there
is a failed check. For Java Maven this caused no templates to be
usable.
Now over the 95% threshold, so `make pr` is passing.
@click.option(
"--no-input",
is_flag=True,
default=False,
help="Disable prompting and accept default values defined template config",
help="Disable Cookiecutter prompting and accept default values defined template config",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you be more verbose on what constitutes template config?

error_msg = """
ERROR: Missing required parameters, with --no-interactive set.

Must provide one of the following required parameter combinations:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[--name--runtime , --dependency-manager, --app-template] or [--location]. Just having all the ands together in --name and --runtime and --dependency-manager and --app-template made it difficult for me to spot the combination.

return list(templates_by_dep)
return templates

def _init_options_from_bundle(self, runtime, dependency_manager):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bundle -> bundled

if self._no_interactive:
return self._auto_clone
do_clone = click.confirm(
"This process will clone app templates from https://github.com/awslabs/aws-sam-cli-app-templates - is this ok?"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I misread the indentation.

choices = map(str, range(1, len(options) + 1))
choice_num = 1
for o in options:
if o.get("displayName") is not None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is displayname part of cookiecutter options? in the cookiecutter.json file. I see it used in the manifest in a test below. Never mind looks to be part of manifest.json

https://github.com/awslabs/aws-sam-cli-app-templates/blob/master/manifest.json

for mapping in list(itertools.chain(*(RUNTIME_DEP_TEMPLATE_MAPPING.values()))):
if runtime in mapping["runtimes"] or any([r.startswith(runtime) for r in mapping["runtimes"]]):
if not dependency_manager or dependency_manager == mapping["dependency_manager"]:
mapping["appTemplate"] = "hello-world" # when bundled, use this default template name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want the logic of falling back to the bundled be driven by manifest in an external repository? https://github.com/awslabs/aws-sam-cli-app-templates/blob/master/manifest.json#L102

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure that's possible. If we get to this piece of code, the manifest is not accessible. Or is this a separate scenario you're thinking about?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I was just thinking that falling back to bundled templates (within the same repo), required direction from a manifest file in another repo (app templates repo), introduces some coupling between the repos.

[self._git_executable(), "clone", self._repo_url], cwd=shared_dir, stderr=subprocess.STDOUT
)
self.repo_path = expected_path
except OSError:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we catch permissions errors as well within OSError?

Copy link
Member Author

@awood45 awood45 Oct 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per its documentation, I believe that it should.

except OSError:
click.echo("WARN: Can't clone app repo, git executable not found.")
except subprocess.CalledProcessError as clone_error:
output = clone_error.output.decode("utf-8")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just look at exit codes instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows the pattern we use for the AWS CLI, what are you proposing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aah never mind, looks like CalledProcessError actually looks at exit codes natively :)

samcli/commands/init/interactive_init_flow.py Outdated Show resolved Hide resolved
tests/unit/commands/init/test_templates.py Outdated Show resolved Hide resolved
from samcli.lib.telemetry.metrics import track_command
from samcli.local.common.runtime_template import INIT_RUNTIMES, SUPPORTED_DEP_MANAGERS, DEFAULT_RUNTIME
from samcli.commands.init.init_generator import do_generate
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these imports should be pushed down into the do_cli function. This is to keep --help text load time low, otherwise we will be loading all of these modules.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which imports?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to create other problems, we can discuss in private.

Slashes behave differently.
Checking integration test behavior.
Had a bug where the wrong executable name would lead to a premature
exit. This change fixes that.
Will better align with errors in place.
This is a WIP to fix breaking Windows integ tests.
This should have been part of the last commit.
If the shared directory doesn't exist, create it before cloning the
template git repo. This will need to be refactored as some layer code
does the same thing.
Init code assumed existence of shared directory, which is likely but not
guaranteed.
@awood45 awood45 merged commit 4d9a3a0 into aws:develop Oct 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants