-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Interactive Init #1454
Conversation
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.
…o interactive-init
There was a problem hiding this 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: |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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?" |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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?" |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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/__init__.py
Outdated
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which imports?
There was a problem hiding this comment.
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.
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.