samcli/commands/build/command.py (223 lines of code) (raw):

""" CLI command for "build" command """ import logging import os from typing import Dict, List, Optional, Tuple import click from samcli.cli.cli_config_file import ConfigProvider, configuration_option, save_params_option from samcli.cli.context import Context from samcli.cli.main import aws_creds_options, pass_context, print_cmdline_args from samcli.cli.main import common_options as cli_framework_options from samcli.commands._utils.option_value_processor import process_env_var, process_image_options from samcli.commands._utils.options import ( base_dir_option, build_dir_option, build_image_option, build_in_source_option, cache_dir_option, cached_option, container_env_var_file_option, docker_common_options, hook_name_click_option, manifest_option, mount_symlinks_option, parameter_override_option, skip_prepare_infra_option, template_option_without_build, terraform_project_root_path_option, use_container_build_option, ) from samcli.commands.build.click_container import ContainerOptions from samcli.commands.build.core.command import BuildCommand from samcli.commands.build.utils import MountMode from samcli.lib.telemetry.metric import track_command from samcli.lib.utils.version_checker import check_newer_version LOG = logging.getLogger(__name__) HELP_TEXT = """ Build AWS serverless function code. """ DESCRIPTION = """ Build AWS serverless function code to generate artifacts targeting AWS Lambda execution environment.\n \b Supported Resource Types ------------------------ 1. AWS::Serverless::Function\n 2. AWS::Lambda::Function\n 3. AWS::Serverless::LayerVersion\n 4. AWS::Lambda::LayerVersion\n \b Supported Runtimes ------------------ 1. Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 using PIP\n 2. Nodejs 22.x, Nodejs 20.x, 18.x, 16.x, 14.x, 12.x using NPM\n 3. Ruby 3.2, 3.3, 3.4 using Bundler\n 4. Java 8, Java 11, Java 17, Java 21 using Gradle and Maven\n 5. Dotnet8, Dotnet6 using Dotnet CLI\n 6. Go 1.x using Go Modules (without --use-container)\n """ @click.command( "build", cls=BuildCommand, help=HELP_TEXT, description=DESCRIPTION, requires_credentials=False, short_help=HELP_TEXT, context_settings={"max_content_width": 120}, ) @configuration_option(provider=ConfigProvider(section="parameters")) @terraform_project_root_path_option @hook_name_click_option( force_prepare=True, invalid_coexist_options=["t", "template-file", "template", "parameter-overrides", "build-in-source"], ) @skip_prepare_infra_option @use_container_build_option @build_in_source_option @click.option( "--container-env-var", "-e", default=None, multiple=True, # Can pass in multiple env vars required=False, help="Environment variables to be passed into build containers" "\nResource format (FuncName.VarName=Value) or Global format (VarName=Value)." "\n\n Example: --container-env-var Func1.VAR1=value1 --container-env-var VAR2=value2", cls=ContainerOptions, ) @container_env_var_file_option(cls=ContainerOptions) @build_image_option(cls=ContainerOptions) @click.option( "--exclude", "-x", default=None, multiple=True, # Multiple resources can be excepted from the build help="Name of the resource(s) to exclude from AWS SAM CLI build.", ) @click.option( "--parallel", "-p", is_flag=True, help="Enable parallel builds for AWS SAM template's functions and layers." ) @click.option( "--mount-with", "-mw", type=click.Choice(MountMode.values(), case_sensitive=False), default=MountMode.READ.value, help="Specify mount mode for building functions/layers inside container. " "If it is mounted with write permissions, some files in source code directory may " "be changed/added by the build process. By default the source code directory is read only.", cls=ContainerOptions, ) @mount_symlinks_option @build_dir_option @cache_dir_option @base_dir_option @manifest_option @cached_option @template_option_without_build @parameter_override_option @docker_common_options @cli_framework_options @aws_creds_options @click.argument("resource_logical_id", required=False) @save_params_option @pass_context @track_command @check_newer_version @print_cmdline_args def cli( ctx: Context, # please keep the type below consistent with @click.options resource_logical_id: Optional[str], template_file: str, base_dir: Optional[str], build_dir: str, cache_dir: str, use_container: bool, cached: bool, parallel: bool, manifest: Optional[str], docker_network: Optional[str], container_env_var: Optional[Tuple[str]], container_env_var_file: Optional[str], build_image: Optional[Tuple[str]], exclude: Optional[Tuple[str, ...]], skip_pull_image: bool, parameter_overrides: dict, config_file: str, config_env: str, save_params: bool, hook_name: Optional[str], skip_prepare_infra: bool, mount_with: str, terraform_project_root_path: Optional[str], build_in_source: Optional[bool], mount_symlinks: Optional[bool], ) -> None: """ `sam build` command entry point """ # All logic must be implemented in the ``do_cli`` method. This helps with easy unit testing mode = _get_mode_value_from_envvar("SAM_BUILD_MODE", choices=["debug"]) do_cli( ctx, resource_logical_id, template_file, base_dir, build_dir, cache_dir, True, use_container, cached, parallel, manifest, docker_network, skip_pull_image, parameter_overrides, mode, container_env_var, container_env_var_file, build_image, exclude, hook_name, build_in_source, mount_with, mount_symlinks, ) # pragma: no cover def do_cli( # pylint: disable=too-many-locals, too-many-statements click_ctx, function_identifier: Optional[str], template: str, base_dir: Optional[str], build_dir: str, cache_dir: str, clean: bool, use_container: bool, cached: bool, parallel: bool, manifest_path: Optional[str], docker_network: Optional[str], skip_pull_image: bool, parameter_overrides: Dict, mode: Optional[str], container_env_var: Optional[Tuple[str]], container_env_var_file: Optional[str], build_image: Optional[Tuple[str]], exclude: Optional[Tuple[str, ...]], hook_name: Optional[str], build_in_source: Optional[bool], mount_with: str, mount_symlinks: Optional[bool], ) -> None: """ Implementation of the ``cli`` method """ from samcli.commands.build.build_context import BuildContext LOG.debug("'build' command is called") if cached: LOG.info("Starting Build use cache") if use_container: LOG.info("Starting Build inside a container") processed_env_vars = process_env_var(container_env_var) processed_build_images = process_image_options(build_image) with BuildContext( function_identifier, template, base_dir, build_dir, cache_dir, cached, parallel=parallel, clean=clean, manifest_path=manifest_path, use_container=use_container, parameter_overrides=parameter_overrides, docker_network=docker_network, skip_pull_image=skip_pull_image, mode=mode, container_env_var=processed_env_vars, container_env_var_file=container_env_var_file, build_images=processed_build_images, excluded_resources=exclude, aws_region=click_ctx.region, hook_name=hook_name, build_in_source=build_in_source, mount_with=mount_with, mount_symlinks=mount_symlinks, ) as ctx: ctx.run() def _get_mode_value_from_envvar(name: str, choices: List[str]) -> Optional[str]: mode = os.environ.get(name, None) if not mode: return None if mode not in choices: raise click.UsageError("Invalid value for 'mode': invalid choice: {}. (choose from {})".format(mode, choices)) return mode