Skip to content

Commit

Permalink
Merge pull request #1051 from awslabs/develop
Browse files Browse the repository at this point in the history
SAM CLI v0.13 release
  • Loading branch information
jfuss committed Mar 11, 2019
2 parents 7ddb5e4 + a6c845c commit d88100f
Show file tree
Hide file tree
Showing 42 changed files with 1,034 additions and 180 deletions.
21 changes: 14 additions & 7 deletions designs/sam_publish_cmd.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ Create new version of an existing SAR application
Click the link below to view your application in AWS console:
https://console.aws.amazon.com/serverlessrepo/home?region=<region>#/published-applications/<arn>

Alternatively, you can provide the new version number through the --semantic-version option without manually modifying
the template. The command will publish a new application version using the specified value.

>>> sam publish -t ./packaged.yaml --semantic-version 0.0.2

Update the metadata of an existing application without creating new version
Keep SemanticVersion unchanged, then modify metadata fields like Description or ReadmeUrl, and run
``sam publish -t ./packaged.yaml``. SAM CLI prints application metadata updated message, values of updated
Expand Down Expand Up @@ -184,13 +189,15 @@ CLI Changes
$ sam publish -t packaged.yaml --region <region>
Options:
-t, --template PATH AWS SAM template file [default: template.[yaml|yml]]
--profile TEXT Select a specific profile from your credential file to
get AWS credentials.
--region TEXT Set the AWS Region of the service (e.g. us-east-1).
--debug Turn on debug logging to print debug message generated
by SAM CLI.
--help Show this message and exit.
-t, --template PATH AWS SAM template file [default: template.[yaml|yml]]
--semantic-version TEXT Optional. The value provided here overrides SemanticVersion
in the template metadata.
--profile TEXT Select a specific profile from your credential file to
get AWS credentials.
--region TEXT Set the AWS Region of the service (e.g. us-east-1).
--debug Turn on debug logging to print debug message generated
by SAM CLI.
--help Show this message and exit.
2. Update ``sam package`` (``aws cloudformation package``) command to support uploading locally referenced readme and
license files to S3.
Expand Down
6 changes: 3 additions & 3 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Flask~=1.0.2
boto3~=1.9, >=1.9.56
PyYAML~=3.12
cookiecutter~=1.6.0
aws-sam-translator==1.9.1
aws-sam-translator==1.10.0
docker>=3.3.0
dateparser~=0.7
python-dateutil~=2.6
pathlib2~=2.3.2; python_version<"3.4"
requests==2.20.1
aws_lambda_builders==0.1.0
serverlessrepo==0.1.5
aws_lambda_builders==0.2.0
serverlessrepo==0.1.8
2 changes: 1 addition & 1 deletion samcli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
SAM CLI version
"""

__version__ = '0.12.0'
__version__ = '0.13.0'
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
65 changes: 28 additions & 37 deletions samcli/commands/publish/command.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
"""CLI command for "publish" command."""

import json
import logging

import click
import boto3
from botocore.exceptions import ClientError
from serverlessrepo import publish_application
from serverlessrepo.publish import CREATE_APPLICATION
from serverlessrepo.exceptions import ServerlessRepoError
from serverlessrepo.parser import METADATA, SERVERLESS_REPO_APPLICATION
from serverlessrepo.exceptions import ServerlessRepoError, InvalidS3UriError

from samcli.cli.main import pass_context, common_options as cli_framework_options, aws_creds_options
from samcli.commands._utils.options import template_common_option
from samcli.commands._utils.template import get_template_data
from samcli.commands.exceptions import UserException

LOG = logging.getLogger(__name__)

SAM_PUBLISH_DOC = "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-publishing-applications.html" # noqa
SAM_PACKAGE_DOC = "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-package.html" # noqa
HELP_TEXT = """
Use this command to publish a packaged AWS SAM template to
the AWS Serverless Application Repository to share within your team,
Expand All @@ -22,46 +27,57 @@
This command expects the template's Metadata section to contain an
AWS::ServerlessRepo::Application section with application metadata
for publishing. For more details on this metadata section, see
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-publishing-applications.html
{}
\b
Examples
--------
To publish an application
$ sam publish -t packaged.yaml --region <region>
"""
""".format(SAM_PUBLISH_DOC)
SHORT_HELP = "Publish a packaged AWS SAM template to the AWS Serverless Application Repository."
SERVERLESSREPO_CONSOLE_URL = "https://console.aws.amazon.com/serverlessrepo/home?region={}#/published-applications/{}"
SEMANTIC_VERSION_HELP = "Optional. The value provided here overrides SemanticVersion in the template metadata."
SEMANTIC_VERSION = 'SemanticVersion'


@click.command("publish", help=HELP_TEXT, short_help=SHORT_HELP)
@template_common_option
@click.option('--semantic-version', help=SEMANTIC_VERSION_HELP)
@aws_creds_options
@cli_framework_options
@pass_context
def cli(ctx, template):
def cli(ctx, template, semantic_version):
# All logic must be implemented in the ``do_cli`` method. This helps with easy unit testing

do_cli(ctx, template) # pragma: no cover
do_cli(ctx, template, semantic_version) # pragma: no cover


def do_cli(ctx, template):
def do_cli(ctx, template, semantic_version):
"""Publish the application based on command line inputs."""
try:
template_data = get_template_data(template)
except ValueError as ex:
click.secho("Publish Failed", fg='red')
raise UserException(str(ex))

# Override SemanticVersion in template metadata when provided in command input
if semantic_version and SERVERLESS_REPO_APPLICATION in template_data.get(METADATA, {}):
template_data.get(METADATA).get(SERVERLESS_REPO_APPLICATION)[SEMANTIC_VERSION] = semantic_version

try:
publish_output = publish_application(template_data)
click.secho("Publish Succeeded", fg="green")
click.secho(_gen_success_message(publish_output), fg="yellow")
except ServerlessRepoError as ex:
click.secho(_gen_success_message(publish_output))
except InvalidS3UriError:
click.secho("Publish Failed", fg='red')
raise UserException(str(ex))
except ClientError as ex:
raise UserException(
"Your SAM template contains invalid S3 URIs. Please make sure that you have uploaded application "
"artifacts to S3 by packaging the template. See more details in {}".format(SAM_PACKAGE_DOC))
except ServerlessRepoError as ex:
click.secho("Publish Failed", fg='red')
raise _wrap_s3_uri_exception(ex)
LOG.debug("Failed to publish application to serverlessrepo", exc_info=True)
error_msg = '{}\nPlease follow the instructions in {}'.format(str(ex), SAM_PUBLISH_DOC)
raise UserException(error_msg)

application_id = publish_output.get('application_id')
_print_console_link(ctx.region, application_id)
Expand Down Expand Up @@ -108,28 +124,3 @@ def _print_console_link(region, application_id):
console_link = SERVERLESSREPO_CONSOLE_URL.format(region, application_id.replace('/', '~'))
msg = "Click the link below to view your application in AWS console:\n{}".format(console_link)
click.secho(msg, fg="yellow")


def _wrap_s3_uri_exception(ex):
"""
Wrap invalid S3 URI exception with a better error message.
Parameters
----------
ex : ClientError
boto3 exception
Returns
-------
Exception
UserException if found invalid S3 URI or ClientError
"""
error_code = ex.response.get('Error').get('Code')
message = ex.response.get('Error').get('Message')

if error_code == 'BadRequestException' and "Invalid S3 URI" in message:
return UserException(
"Your SAM template contains invalid S3 URIs. Please make sure that you have uploaded application "
"artifacts to S3 by packaging the template: 'sam package --template-file <file-path>'.")

return ex
25 changes: 3 additions & 22 deletions samcli/commands/validate/lib/sam_template_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,10 @@ def is_valid(self):

self._replace_local_codeuri()

# In the Paser class, within the SAM Translator, they log a warning for when the template
# does not match the schema. The logger they use is the root logger instead of one scoped to
# their module. Currently this does not cause templates to fail, so we will suppress this
# by patching the logging.warning method that is used in that class.
class WarningSuppressLogger(object):

def __init__(self, obj_to_patch):
self.obj_to_patch = obj_to_patch

def __enter__(self):
self.obj_to_patch.warning = self.warning

def __exit__(self, exc_type, exc_val, exc_tb):
self.obj_to_patch.warning = self.obj_to_patch.warning

def warning(self, message):
pass

try:
with WarningSuppressLogger(parser.logging):
template = sam_translator.translate(sam_template=self.sam_template,
parameter_values={})
LOG.debug("Translated template is:\n%s", yaml_dump(template))
template = sam_translator.translate(sam_template=self.sam_template,
parameter_values={})
LOG.debug("Translated template is:\n%s", yaml_dump(template))
except InvalidDocumentException as e:
raise InvalidSamDocumentException(
functools.reduce(lambda message, error: message + ' ' + str(error), e.causes, str(e)))
Expand Down
10 changes: 9 additions & 1 deletion samcli/lib/build/workflow_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@
manifest_name="build.gradle",
executable_search_paths=None)

JAVA_MAVEN_CONFIG = CONFIG(
language="java",
dependency_manager="maven",
application_framework=None,
manifest_name="pom.xml",
executable_search_paths=None)


class UnsupportedRuntimeException(Exception):
pass
Expand Down Expand Up @@ -83,7 +90,8 @@ def get_workflow_config(runtime, code_dir, project_dir):
# manifest
"java8": ManifestWorkflowSelector([
# Gradle builder needs custom executable paths to find `gradlew` binary
JAVA_GRADLE_CONFIG._replace(executable_search_paths=[code_dir, project_dir])
JAVA_GRADLE_CONFIG._replace(executable_search_paths=[code_dir, project_dir]),
JAVA_MAVEN_CONFIG
]),
}

Expand Down
Empty file added samcli/local/common/__init__.py
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 = {
"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": True
},
{
"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(
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())
Loading

0 comments on commit d88100f

Please sign in to comment.