import os import shutil import subprocess 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 "uv build", needs to be run before running this script AWS_DEPENDENCY_GROUP = "aws" 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: str, out_zip_filename: "Optional[str]" = None, ) -> 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) -> None: os.makedirs(self.python_site_packages) def install_python_packages(self) -> 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( [ "uv", "pip", "install", "--group", AWS_DEPENDENCY_GROUP, "--target", self.python_site_packages, ], ) sentry_python_sdk = os.path.join( DIST_PATH, f"sentry_sdk-{SDK_VERSION}-py3-none-any.whl", ) subprocess.run( [ "uv", "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) -> 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/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) -> 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, out_zip_filename=None): if base_dir is None: base_dir = tempfile.mkdtemp() 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(" - Package zip filename: {}".format(layer_builder.out_zip_filename)) print(" - Copied package zip to: {}".format(dist_path)) if __name__ == "__main__": build_packaged_zip()