import os import shutil import subprocess import sys import tempfile from typing import TYPE_CHECKING from sentry_sdk.consts import VERSION as SDK_VERSION if TYPE_CHECKING: from typing import Optional DIST_PATH = "dist" # created by "make dist" that is called by "make aws-lambda-layer" PYTHON_SITE_PACKAGES = "python" # see https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-path class LayerBuilder: def __init__( self, base_dir, # type: str out_zip_filename=None, # type: Optional[str] ): # type: (...) -> None self.base_dir = base_dir self.python_site_packages = os.path.join(self.base_dir, PYTHON_SITE_PACKAGES) self.out_zip_filename = ( f"sentry-python-serverless-{SDK_VERSION}.zip" if out_zip_filename is None else out_zip_filename ) def make_directories(self): # type: (...) -> None os.makedirs(self.python_site_packages) def install_python_packages(self): # type: (...) -> None # Install requirements for Lambda Layer (these are more limited than the SDK requirements, # because Lambda does not support the newest versions of some packages) subprocess.check_call( [ sys.executable, "-m", "pip", "install", "-r", "requirements-aws-lambda-layer.txt", "--target", self.python_site_packages, ], ) sentry_python_sdk = os.path.join( DIST_PATH, f"sentry_sdk-{SDK_VERSION}-py2.py3-none-any.whl", # this is generated by "make dist" that is called by "make aws-lambda-layer" ) subprocess.run( [ "pip", "install", "--no-cache-dir", # always access PyPI "--no-deps", # the right depencencies have been installed in the call above "--quiet", sentry_python_sdk, "--target", self.python_site_packages, ], check=True, ) def create_init_serverless_sdk_package(self): # type: (...) -> None """ Method that creates the init_serverless_sdk pkg in the sentry-python-serverless zip """ serverless_sdk_path = ( f"{self.python_site_packages}/sentry_sdk/" f"integrations/init_serverless_sdk" ) if not os.path.exists(serverless_sdk_path): os.makedirs(serverless_sdk_path) shutil.copy( "scripts/init_serverless_sdk.py", f"{serverless_sdk_path}/__init__.py" ) def zip(self): # type: (...) -> None subprocess.run( [ "zip", "-q", # Quiet "-x", # Exclude files "**/__pycache__/*", # Files to be excluded "-r", # Recurse paths self.out_zip_filename, # Output filename PYTHON_SITE_PACKAGES, # Files to be zipped ], cwd=self.base_dir, check=True, # Raises CalledProcessError if exit status is non-zero ) shutil.copy( os.path.join(self.base_dir, self.out_zip_filename), os.path.abspath(DIST_PATH), ) def build_packaged_zip(base_dir=None, make_dist=False, out_zip_filename=None): if base_dir is None: base_dir = tempfile.mkdtemp() if make_dist: # Same thing that is done by "make dist" # (which is a dependency of "make aws-lambda-layer") subprocess.check_call( [sys.executable, "setup.py", "sdist", "bdist_wheel", "-d", DIST_PATH], ) layer_builder = LayerBuilder(base_dir, out_zip_filename=out_zip_filename) layer_builder.make_directories() layer_builder.install_python_packages() layer_builder.create_init_serverless_sdk_package() layer_builder.zip() # Just for debugging dist_path = os.path.abspath(DIST_PATH) print("Created Lambda Layer package with this information:") print(" - Base directory for generating package: {}".format(layer_builder.base_dir)) print( " - Created Python SDK distribution (in `{}`): {}".format(dist_path, make_dist) ) if not make_dist: print(" If 'False' we assume it was already created (by 'make dist')") print(" - Package zip filename: {}".format(layer_builder.out_zip_filename)) print(" - Copied package zip to: {}".format(dist_path)) if __name__ == "__main__": build_packaged_zip()