Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions samcli/commands/init/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,31 @@
Init command to scaffold a project app from a template
"""
import logging

import click

from samcli.cli.main import pass_context, common_options
from samcli.commands.exceptions import UserException
from samcli.local.common.runtime_template import INIT_RUNTIMES, SUPPORTED_DEP_MANAGERS
from samcli.local.init import generate_project
from samcli.local.init import RUNTIME_TEMPLATE_MAPPING
from samcli.local.init.exceptions import GenerateProjectFailedError
from samcli.commands.exceptions import UserException

LOG = logging.getLogger(__name__)
SUPPORTED_RUNTIME = [r for r in RUNTIME_TEMPLATE_MAPPING]


@click.command(context_settings=dict(help_option_names=[u'-h', u'--help']))
@click.option('-l', '--location', help="Template location (git, mercurial, http(s), zip, path)")
@click.option('-r', '--runtime', type=click.Choice(SUPPORTED_RUNTIME), default="nodejs8.10",
@click.option('-r', '--runtime', type=click.Choice(INIT_RUNTIMES), default="nodejs8.10",
help="Lambda Runtime of your app")
@click.option('-d', '--dependency-manager', type=click.Choice(SUPPORTED_DEP_MANAGERS), default=None,
help="Dependency manager of your Lambda runtime", required=False)
@click.option('-o', '--output-dir', default='.', type=click.Path(), help="Where to output the initialized app into")
@click.option('-n', '--name', default="sam-app", help="Name of your project to be generated as a folder")
@click.option('--no-input', is_flag=True, default=False,
help="Disable prompting and accept default values defined template config")
@common_options
@pass_context
def cli(ctx, location, runtime, output_dir, name, no_input):
def cli(ctx, location, runtime, dependency_manager, output_dir, name, no_input):
""" \b
Initialize a serverless application with a SAM template, folder
structure for your Lambda functions, connected to an event source such as APIs,
Expand All @@ -43,6 +45,10 @@ def cli(ctx, location, runtime, output_dir, name, no_input):
\b
$ sam init --runtime python3.6
\b
Initializes a new SAM project using Java 8 and Gradle dependency manager
\b
$ sam init --runtime java8 --dependency-manager gradle
\b
Initializes a new SAM project using custom template in a Git/Mercurial repository
\b
# gh being expanded to github url
Expand All @@ -66,11 +72,11 @@ def cli(ctx, location, runtime, output_dir, name, no_input):

"""
# All logic must be implemented in the `do_cli` method. This helps ease unit tests
do_cli(ctx, location, runtime, output_dir,
do_cli(ctx, location, runtime, dependency_manager, output_dir,
name, no_input) # pragma: no cover


def do_cli(ctx, location, runtime, output_dir, name, no_input):
def do_cli(ctx, location, runtime, dependency_manager, output_dir, name, no_input):
"""
Implementation of the ``cli`` method, just separated out for unit testing purposes
"""
Expand Down Expand Up @@ -101,7 +107,7 @@ def do_cli(ctx, location, runtime, output_dir, name, no_input):
next_step_msg = no_build_msg if runtime in no_build_step_required else build_msg

try:
generate_project(location, runtime, output_dir, name, no_input)
generate_project(location, runtime, dependency_manager, output_dir, name, no_input)
if not location:
click.secho(next_step_msg, bold=True)
click.secho("Read {name}/README.md for further instructions\n".format(name=name), bold=True)
Expand Down
Empty file.
83 changes: 83 additions & 0 deletions samcli/local/common/runtime_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
All-in-one metadata about runtimes
"""

import itertools
import os

try:
import pathlib
except ImportError:
import pathlib2 as pathlib

_init_path = str(pathlib.Path(os.path.dirname(__file__)).parent)
_templates = os.path.join(_init_path, 'init', 'templates')

RUNTIME_DEP_TEMPLATE_MAPPING = {
Copy link
Contributor

Choose a reason for hiding this comment

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

This does not make sense. Why would you depend on the build workflow configs within init. They should be treated as isolated modules. Otherwise you will end up with a tangled mess of dependencies one year from now.

Copy link
Contributor

Choose a reason for hiding this comment

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

@jfuss filled me in on the intention of this mapping. The config here makes sense (as a true configuration data), but I am not excited about importing workflow_config which is an implementation detail. Can we just remove the workflow configs because they are not really used? That would make me more happy :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will remove that. 👍

"python": [
{
"runtimes": ["python2.7", "python3.6", "python3.7"],
"dependency_manager": "pip",
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-python"),
"build": True
}
],
"ruby": [
{
"runtimes": ["ruby2.5"],
"dependency_manager": "bundler",
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-ruby"),
"build": True
}
],
"nodejs": [
{
"runtimes": ["nodejs8.10"],
"dependency_manager": "npm",
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-nodejs"),
"build": True
},
{
"runtimes": ["nodejs6.10"],
"dependency_manager": "npm",
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-nodejs6"),
"build": True
},
],
"dotnet": [
{
"runtimes": ["dotnetcore", "dotnetcore1.0", "dotnetcore2.0", "dotnetcore2.1"],
"dependency_manager": "cli-package",
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-dotnet"),
"build": False
},
],
"go": [
{
"runtimes": ["go1.x"],
"dependency_manager": None,
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-go"),
"build": False
}
],
"java": [
{
"runtimes": ["java8"],
"dependency_manager": "maven",
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-java-maven"),
"build": False
},
{
"runtimes": ["java8"],
"dependency_manager": "gradle",
"init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-java-gradle"),
"build": True
}
]
}

SUPPORTED_DEP_MANAGERS = set([c['dependency_manager'] for c in list(
Copy link
Contributor

Choose a reason for hiding this comment

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

I understand what this line is doing, but boy there are so many brackets!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, Having a single config, makes extraction a bit more dense.

itertools.chain(*(RUNTIME_DEP_TEMPLATE_MAPPING.values()))) if c['dependency_manager']])
RUNTIMES = set(itertools.chain(*[c['runtimes'] for c in list(
itertools.chain(*(RUNTIME_DEP_TEMPLATE_MAPPING.values())))]))
INIT_RUNTIMES = RUNTIMES.union(RUNTIME_DEP_TEMPLATE_MAPPING.keys())
49 changes: 22 additions & 27 deletions samcli/local/init/__init__.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,20 @@
"""
Init module to scaffold a project app from a template
"""
import itertools
import logging
import os

from cookiecutter.main import cookiecutter
from cookiecutter.exceptions import CookiecutterException
from cookiecutter.main import cookiecutter

from samcli.local.common.runtime_template import RUNTIME_DEP_TEMPLATE_MAPPING
from samcli.local.init.exceptions import GenerateProjectFailedError

LOG = logging.getLogger(__name__)

_init_path = os.path.dirname(__file__)
_templates = os.path.join(_init_path, 'templates')

RUNTIME_TEMPLATE_MAPPING = {
"python3.7": os.path.join(_templates, "cookiecutter-aws-sam-hello-python"),
"python3.6": os.path.join(_templates, "cookiecutter-aws-sam-hello-python"),
"python2.7": os.path.join(_templates, "cookiecutter-aws-sam-hello-python"),
"python": os.path.join(_templates, "cookiecutter-aws-sam-hello-python"),
"ruby2.5": os.path.join(_templates, "cookiecutter-aws-sam-hello-ruby"),
"nodejs6.10": os.path.join(_templates, "cookiecutter-aws-sam-hello-nodejs6"),
"nodejs8.10": os.path.join(_templates, "cookiecutter-aws-sam-hello-nodejs"),
"nodejs": os.path.join(_templates, "cookiecutter-aws-sam-hello-nodejs"),
"dotnetcore2.0": os.path.join(_templates, "cookiecutter-aws-sam-hello-dotnet"),
"dotnetcore2.1": os.path.join(_templates, "cookiecutter-aws-sam-hello-dotnet"),
"dotnetcore1.0": os.path.join(_templates, "cookiecutter-aws-sam-hello-dotnet"),
"dotnetcore": os.path.join(_templates, "cookiecutter-aws-sam-hello-dotnet"),
"dotnet": os.path.join(_templates, "cookiecutter-aws-sam-hello-dotnet"),
"go1.x": os.path.join(_templates, "cookiecutter-aws-sam-hello-golang"),
"go": os.path.join(_templates, "cookiecutter-aws-sam-hello-golang"),
"java8": os.path.join(_templates, "cookiecutter-aws-sam-hello-java"),
"java": os.path.join(_templates, "cookiecutter-aws-sam-hello-java")
}


def generate_project(
location=None, runtime="nodejs",
location=None, runtime="nodejs", dependency_manager=None,
output_dir=".", name='sam-sample-app', no_input=False):
"""Generates project using cookiecutter and options given

Expand All @@ -50,6 +29,8 @@ def generate_project(
(the default is None, which means no custom template)
runtime: str, optional
Lambda Runtime (the default is "nodejs", which creates a nodejs project)
dependency_manager: str, optional
Dependency Manager for the Lambda Runtime Project(the default is "npm" for a "nodejs" Lambda runtime)
output_dir: str, optional
Output directory where project should be generated
(the default is ".", which implies current folder)
Expand All @@ -66,8 +47,22 @@ def generate_project(
If the process of baking a project fails
"""

template = None

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:
template = mapping['init_location']
break
elif dependency_manager == mapping['dependency_manager']:
template = mapping['init_location']

if not template:
msg = "Lambda Runtime {} does not support dependency manager: {}".format(runtime, dependency_manager)
raise GenerateProjectFailedError(project=name, provider_error=msg)

params = {
"template": location if location else RUNTIME_TEMPLATE_MAPPING[runtime],
"template": location if location else template,
"output_dir": output_dir,
"no_input": no_input
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
plugins {
id 'java'
}

repositories {
mavenCentral()
}

dependencies {
implementation 'software.amazon.awssdk:annotations:2.1.0'
compile (
'com.amazonaws:aws-lambda-java-core:1.1.0'
)
testCompile 'junit:junit:4.12'
}

sourceSets {
main {
java {
srcDir 'src/main'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading