aws_lbd_art_builder_uv 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. aws_lbd_art_builder_uv-0.1.1/AUTHORS.rst +15 -0
  2. aws_lbd_art_builder_uv-0.1.1/LICENSE.txt +21 -0
  3. aws_lbd_art_builder_uv-0.1.1/PKG-INFO +142 -0
  4. aws_lbd_art_builder_uv-0.1.1/README.rst +85 -0
  5. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/__init__.py +1 -0
  6. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/api.py +3 -0
  7. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/layer/__init__.py +2 -0
  8. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/layer/_build_in_container.py +183 -0
  9. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/layer/api.py +19 -0
  10. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/layer/container_builder.py +177 -0
  11. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/layer/local_builder.py +173 -0
  12. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/layer/validate.py +193 -0
  13. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/paths.py +63 -0
  14. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/vendor/__init__.py +2 -0
  15. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv/vendor/pytest_cov_helper.py +148 -0
  16. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv.egg-info/PKG-INFO +142 -0
  17. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv.egg-info/SOURCES.txt +21 -0
  18. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv.egg-info/dependency_links.txt +1 -0
  19. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv.egg-info/requires.txt +35 -0
  20. aws_lbd_art_builder_uv-0.1.1/aws_lbd_art_builder_uv.egg-info/top_level.txt +1 -0
  21. aws_lbd_art_builder_uv-0.1.1/pyproject.toml +139 -0
  22. aws_lbd_art_builder_uv-0.1.1/setup.cfg +4 -0
  23. aws_lbd_art_builder_uv-0.1.1/tests/test_api.py +18 -0
@@ -0,0 +1,15 @@
1
+ .. _about_author:
2
+
3
+ About the Author
4
+ ------------------------------------------------------------------------------
5
+ ::
6
+
7
+ (\ (\
8
+ ( -.-)o
9
+ o_(")(")
10
+
11
+ **Sanhe Hu** is a seasoned software engineer with a deep passion for Python development since 2010. As an author and maintainer of `150+ open-source Python projects <https://pypi.org/user/machugwu/>`_, with over `15 million monthly downloads <https://github.com/MacHu-GWU>`_, I bring a wealth of experience to the table. As a Senior Solution Architect and Subject Matter Expert in AI, Data, Amazon Web Services, Cloud Engineering, DevOps, I thrive on helping clients with platform design, enterprise architecture, and strategic roadmaps.
12
+
13
+ Talk is cheap, show me the code:
14
+
15
+ - My Github: https://github.com/MacHu-GWU
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sanhe Hu <husanhe@email.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: aws_lbd_art_builder_uv
3
+ Version: 0.1.1
4
+ Summary: AWS Lambda Artifact UV Builder
5
+ Author-email: Sanhe Hu <husanhe@email.com>
6
+ Maintainer-email: Sanhe Hu <husanhe@email.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project
9
+ Project-URL: Documentation, https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/
10
+ Project-URL: Repository, https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project
11
+ Project-URL: Issues, https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/issues
12
+ Project-URL: Changelog, https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/blob/main/release-history.rst
13
+ Project-URL: Download, https://pypi.org/pypi/aws-lbd-art-builder-uv#files
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: Unix
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Requires-Python: <4.0,>=3.11
26
+ Description-Content-Type: text/x-rst
27
+ License-File: LICENSE.txt
28
+ License-File: AUTHORS.rst
29
+ Requires-Dist: aws-lbd-art-builder-core<1.0.0,>=0.1.4
30
+ Provides-Extra: upload
31
+ Requires-Dist: aws-lbd-art-builder-core[upload]; extra == "upload"
32
+ Provides-Extra: publish
33
+ Requires-Dist: aws-lbd-art-builder-core[publish]; extra == "publish"
34
+ Provides-Extra: dev
35
+ Requires-Dist: rich<14.0.0,>=13.8.1; extra == "dev"
36
+ Provides-Extra: test
37
+ Requires-Dist: pytest<9.0.0,>=8.2.2; extra == "test"
38
+ Requires-Dist: pytest-cov<7.0.0,>=6.0.0; extra == "test"
39
+ Requires-Dist: moto<6.0.0,>=5.1.10; extra == "test"
40
+ Requires-Dist: aws-lbd-art-builder-core[publish,upload]; extra == "test"
41
+ Provides-Extra: doc
42
+ Requires-Dist: Sphinx<8.0.0,>=7.4.7; extra == "doc"
43
+ Requires-Dist: sphinx-copybutton<1.0.0,>=0.5.2; extra == "doc"
44
+ Requires-Dist: sphinx-design<1.0.0,>=0.6.1; extra == "doc"
45
+ Requires-Dist: sphinx-jinja<3.0.0,>=2.0.2; extra == "doc"
46
+ Requires-Dist: furo==2024.8.6; extra == "doc"
47
+ Requires-Dist: pygments<3.0.0,>=2.18.0; extra == "doc"
48
+ Requires-Dist: ipython<8.19.0,>=8.18.1; extra == "doc"
49
+ Requires-Dist: nbsphinx<1.0.0,>=0.8.12; extra == "doc"
50
+ Requires-Dist: rstobj==2.0.0; extra == "doc"
51
+ Requires-Dist: docfly==3.0.3; extra == "doc"
52
+ Provides-Extra: mise
53
+ Requires-Dist: PyGithub<3.0.0,>=2.8.0; extra == "mise"
54
+ Requires-Dist: httpx<1.0.0,>=0.28.0; extra == "mise"
55
+ Requires-Dist: tomli<3.0.0,>=2.0.0; python_version < "3.11" and extra == "mise"
56
+ Dynamic: license-file
57
+
58
+
59
+ .. image:: https://readthedocs.org/projects/aws-lbd-art-builder-uv/badge/?version=latest
60
+ :target: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/
61
+ :alt: Documentation Status
62
+
63
+ .. image:: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/actions/workflows/main.yml/badge.svg
64
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/actions?query=workflow:CI
65
+
66
+ .. image:: https://codecov.io/gh/MacHu-GWU/aws_lbd_art_builder_uv-project/branch/main/graph/badge.svg
67
+ :target: https://codecov.io/gh/MacHu-GWU/aws_lbd_art_builder_uv-project
68
+
69
+ .. image:: https://img.shields.io/pypi/v/aws-lbd-art-builder-uv.svg
70
+ :target: https://pypi.python.org/pypi/aws-lbd-art-builder-uv
71
+
72
+ .. image:: https://img.shields.io/pypi/l/aws-lbd-art-builder-uv.svg
73
+ :target: https://pypi.python.org/pypi/aws-lbd-art-builder-uv
74
+
75
+ .. image:: https://img.shields.io/pypi/pyversions/aws-lbd-art-builder-uv.svg
76
+ :target: https://pypi.python.org/pypi/aws-lbd-art-builder-uv
77
+
78
+ .. image:: https://img.shields.io/badge/✍️_Release_History!--None.svg?style=social&logo=github
79
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/blob/main/release-history.rst
80
+
81
+ .. image:: https://img.shields.io/badge/⭐_Star_me_on_GitHub!--None.svg?style=social&logo=github
82
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project
83
+
84
+ ------
85
+
86
+ .. image:: https://img.shields.io/badge/Link-API-blue.svg
87
+ :target: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/py-modindex.html
88
+
89
+ .. image:: https://img.shields.io/badge/Link-Install-blue.svg
90
+ :target: `install`_
91
+
92
+ .. image:: https://img.shields.io/badge/Link-GitHub-blue.svg
93
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project
94
+
95
+ .. image:: https://img.shields.io/badge/Link-Submit_Issue-blue.svg
96
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/issues
97
+
98
+ .. image:: https://img.shields.io/badge/Link-Request_Feature-blue.svg
99
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/issues
100
+
101
+ .. image:: https://img.shields.io/badge/Link-Download-blue.svg
102
+ :target: https://pypi.org/pypi/aws-lbd-art-builder-uv#files
103
+
104
+
105
+ Welcome to ``aws_lbd_art_builder_uv`` Documentation
106
+ ==============================================================================
107
+ .. image:: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/_static/aws_lbd_art_builder_uv-logo.png
108
+ :target: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/
109
+
110
+ ``aws_lbd_art_builder_uv`` is the **uv** backend for `aws_lbd_art_builder_core <https://github.com/MacHu-GWU/aws_lbd_art_builder_core-project>`_, providing automated AWS Lambda layer builds using `uv <https://docs.astral.sh/uv/>`_ as the package manager.
111
+
112
+ It offers two builders:
113
+
114
+ - **UvLambdaLayerLocalBuilder** — runs ``uv sync`` directly on the host machine. Fast, no Docker required. Best for pure-Python dependencies.
115
+ - **UvLambdaLayerContainerBuilder** — runs ``uv sync`` inside an `AWS SAM Docker image <https://gallery.ecr.aws/sam>`_ to produce Linux-compatible binaries. Required when your layer includes packages with C extensions (e.g. ``numpy``, ``pandas``).
116
+
117
+ Both builders follow a 4-step workflow: **Preflight Check → Prepare Environment → Execute Build → Finalize Artifacts**. The output is a ``artifacts/python/`` directory ready to be zipped and published as a Lambda layer. Combine with ``aws_lbd_art_builder_core``'s package, upload, and publish steps for a complete deployment pipeline.
118
+
119
+ Features:
120
+
121
+ - Reproducible builds via ``uv sync --frozen`` with lock files
122
+ - Private PyPI index support (e.g. AWS CodeArtifact) via credential injection
123
+ - Artifact validation — verify installed packages match ``pyproject.toml`` dependencies
124
+ - Pure-stdlib container script — no pip install needed inside the container
125
+
126
+
127
+ .. _install:
128
+
129
+ Install
130
+ ------------------------------------------------------------------------------
131
+
132
+ ``aws_lbd_art_builder_uv`` is released on PyPI, so all you need is to:
133
+
134
+ .. code-block:: console
135
+
136
+ $ pip install aws-lbd-art-builder-uv
137
+
138
+ To upgrade to latest version:
139
+
140
+ .. code-block:: console
141
+
142
+ $ pip install --upgrade aws-lbd-art-builder-uv
@@ -0,0 +1,85 @@
1
+
2
+ .. image:: https://readthedocs.org/projects/aws-lbd-art-builder-uv/badge/?version=latest
3
+ :target: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/
4
+ :alt: Documentation Status
5
+
6
+ .. image:: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/actions/workflows/main.yml/badge.svg
7
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/actions?query=workflow:CI
8
+
9
+ .. image:: https://codecov.io/gh/MacHu-GWU/aws_lbd_art_builder_uv-project/branch/main/graph/badge.svg
10
+ :target: https://codecov.io/gh/MacHu-GWU/aws_lbd_art_builder_uv-project
11
+
12
+ .. image:: https://img.shields.io/pypi/v/aws-lbd-art-builder-uv.svg
13
+ :target: https://pypi.python.org/pypi/aws-lbd-art-builder-uv
14
+
15
+ .. image:: https://img.shields.io/pypi/l/aws-lbd-art-builder-uv.svg
16
+ :target: https://pypi.python.org/pypi/aws-lbd-art-builder-uv
17
+
18
+ .. image:: https://img.shields.io/pypi/pyversions/aws-lbd-art-builder-uv.svg
19
+ :target: https://pypi.python.org/pypi/aws-lbd-art-builder-uv
20
+
21
+ .. image:: https://img.shields.io/badge/✍️_Release_History!--None.svg?style=social&logo=github
22
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/blob/main/release-history.rst
23
+
24
+ .. image:: https://img.shields.io/badge/⭐_Star_me_on_GitHub!--None.svg?style=social&logo=github
25
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project
26
+
27
+ ------
28
+
29
+ .. image:: https://img.shields.io/badge/Link-API-blue.svg
30
+ :target: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/py-modindex.html
31
+
32
+ .. image:: https://img.shields.io/badge/Link-Install-blue.svg
33
+ :target: `install`_
34
+
35
+ .. image:: https://img.shields.io/badge/Link-GitHub-blue.svg
36
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project
37
+
38
+ .. image:: https://img.shields.io/badge/Link-Submit_Issue-blue.svg
39
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/issues
40
+
41
+ .. image:: https://img.shields.io/badge/Link-Request_Feature-blue.svg
42
+ :target: https://github.com/MacHu-GWU/aws_lbd_art_builder_uv-project/issues
43
+
44
+ .. image:: https://img.shields.io/badge/Link-Download-blue.svg
45
+ :target: https://pypi.org/pypi/aws-lbd-art-builder-uv#files
46
+
47
+
48
+ Welcome to ``aws_lbd_art_builder_uv`` Documentation
49
+ ==============================================================================
50
+ .. image:: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/_static/aws_lbd_art_builder_uv-logo.png
51
+ :target: https://aws-lbd-art-builder-uv.readthedocs.io/en/latest/
52
+
53
+ ``aws_lbd_art_builder_uv`` is the **uv** backend for `aws_lbd_art_builder_core <https://github.com/MacHu-GWU/aws_lbd_art_builder_core-project>`_, providing automated AWS Lambda layer builds using `uv <https://docs.astral.sh/uv/>`_ as the package manager.
54
+
55
+ It offers two builders:
56
+
57
+ - **UvLambdaLayerLocalBuilder** — runs ``uv sync`` directly on the host machine. Fast, no Docker required. Best for pure-Python dependencies.
58
+ - **UvLambdaLayerContainerBuilder** — runs ``uv sync`` inside an `AWS SAM Docker image <https://gallery.ecr.aws/sam>`_ to produce Linux-compatible binaries. Required when your layer includes packages with C extensions (e.g. ``numpy``, ``pandas``).
59
+
60
+ Both builders follow a 4-step workflow: **Preflight Check → Prepare Environment → Execute Build → Finalize Artifacts**. The output is a ``artifacts/python/`` directory ready to be zipped and published as a Lambda layer. Combine with ``aws_lbd_art_builder_core``'s package, upload, and publish steps for a complete deployment pipeline.
61
+
62
+ Features:
63
+
64
+ - Reproducible builds via ``uv sync --frozen`` with lock files
65
+ - Private PyPI index support (e.g. AWS CodeArtifact) via credential injection
66
+ - Artifact validation — verify installed packages match ``pyproject.toml`` dependencies
67
+ - Pure-stdlib container script — no pip install needed inside the container
68
+
69
+
70
+ .. _install:
71
+
72
+ Install
73
+ ------------------------------------------------------------------------------
74
+
75
+ ``aws_lbd_art_builder_uv`` is released on PyPI, so all you need is to:
76
+
77
+ .. code-block:: console
78
+
79
+ $ pip install aws-lbd-art-builder-uv
80
+
81
+ To upgrade to latest version:
82
+
83
+ .. code-block:: console
84
+
85
+ $ pip install --upgrade aws-lbd-art-builder-uv
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1,3 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .layer import api as layer_api
@@ -0,0 +1,2 @@
1
+ # -*- coding: utf-8 -*-
2
+
@@ -0,0 +1,183 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ Container-side Lambda layer build script (pure stdlib, Python 3.11+).
5
+
6
+ This script runs **inside** the Docker container launched by
7
+ :class:`~aws_lbd_art_builder_uv.layer.container_builder.UvLambdaLayerContainerBuilder`.
8
+ No third-party packages are needed — only ``uv`` (installed at runtime).
9
+
10
+ **Why pure stdlib?** This script is copied into a bare AWS SAM Docker image
11
+ that has nothing pre-installed except Python itself. If we imported any
12
+ third-party library here, we would have to install it inside the container
13
+ first, adding complexity and build time. Keeping this script stdlib-only
14
+ means the only external tool we need to fetch is ``uv``.
15
+
16
+ The host mounts ``{project_root}/build/lambda/layer/`` → ``/var/task/``.
17
+ Layout inside the container::
18
+
19
+ /var/task/ ← mount point
20
+ ├── build_lambda_layer_in_container.py ← this script (renamed copy)
21
+ ├── private-repository-credentials.json ← optional credentials
22
+ └── repo/
23
+ ├── pyproject.toml
24
+ ├── uv.lock
25
+ └── .venv/ ← created by uv sync
26
+
27
+ The host-side step 4 moves ``repo/.venv/lib/pythonX.Y/site-packages/``
28
+ into ``artifacts/python/`` after the container exits.
29
+
30
+ See ``docs/source/99-Maintainer-Guide/03-Build-Lambda-Layer-using-UV-in-Container``
31
+ for the full architecture guide.
32
+ """
33
+
34
+ import os
35
+ import sys
36
+ import json
37
+ import argparse
38
+ import subprocess
39
+ from pathlib import Path
40
+ from datetime import datetime
41
+
42
+
43
+ # --- Logging helpers (match the host-side BaseLogger format) ------------------
44
+ def _log_sub_header(title: str): # pragma: no cover
45
+ print("")
46
+ print("+----- " + title)
47
+ print("|")
48
+
49
+
50
+ def _log(msg: str): # pragma: no cover
51
+ print("| " + msg)
52
+
53
+
54
+ def parse_args(): # pragma: no cover
55
+ parser = argparse.ArgumentParser(
56
+ description="Build Lambda layer using uv inside a Docker container.",
57
+ )
58
+ parser.add_argument(
59
+ "--dir-task",
60
+ default="/var/task",
61
+ help="The mount point inside the container. Default: /var/task",
62
+ )
63
+ return parser.parse_args()
64
+
65
+
66
+ def main(): # pragma: no cover
67
+ args = parse_args()
68
+ dir_task = Path(args.dir_task)
69
+
70
+ # --- Derived paths ---------------------------------------------------------
71
+ dir_repo = dir_task / "repo"
72
+ path_credentials = dir_task / "private-repository-credentials.json"
73
+
74
+ # --------------------------------------------------------------------------
75
+ # Step 1 — Verify container execution environment
76
+ # --------------------------------------------------------------------------
77
+ _log_sub_header("Container Step 1 - Verify Environment")
78
+ _log(f"dir_task = {dir_task}")
79
+ _log(f"dir_repo = {dir_repo}")
80
+ if not dir_repo.exists():
81
+ raise EnvironmentError(
82
+ f"{dir_repo} does not exist. "
83
+ f"This script expects the host to mount build/lambda/layer/ to {dir_task}."
84
+ )
85
+ _log("Verification OK.")
86
+
87
+ # --------------------------------------------------------------------------
88
+ # Step 2 — Install uv
89
+ #
90
+ # Why install uv at runtime via curl instead of pre-baking it into the
91
+ # Docker image? Because we use the official AWS SAM base image directly
92
+ # (e.g. public.ecr.aws/sam/build-python3.12) to guarantee binary
93
+ # compatibility with the real Lambda runtime. Building a custom image
94
+ # that bundles uv would add a maintenance burden (rebuild on every SAM
95
+ # image update) and defeat the purpose of using the official image.
96
+ # The curl install takes only a few seconds and always gets the latest uv.
97
+ # --------------------------------------------------------------------------
98
+ _log_sub_header("Container Step 2 - Install uv")
99
+ st = datetime.now()
100
+ subprocess.run(
101
+ "curl -LsSf https://astral.sh/uv/install.sh | sh",
102
+ shell=True,
103
+ check=True,
104
+ )
105
+ elapsed = (datetime.now() - st).total_seconds()
106
+ _log(f"install uv elapsed: {elapsed:.2f} seconds")
107
+
108
+ path_bin_uv = Path("/root/.local/bin/uv")
109
+
110
+ # --------------------------------------------------------------------------
111
+ # Step 3 — Setup private repository credentials (optional)
112
+ #
113
+ # Credentials are set up inside the container (not passed via docker env
114
+ # vars) because sensitive tokens should not appear in ``docker run``
115
+ # command lines, which are visible in process listings. Instead, the
116
+ # host dumps a JSON file into the bind-mounted directory, and this
117
+ # script reads it.
118
+ # --------------------------------------------------------------------------
119
+ _log_sub_header("Container Step 3 - Setup Credentials")
120
+ if path_credentials.exists():
121
+ with open(path_credentials, "r") as f:
122
+ cred = json.load(f)
123
+ index_name = cred["index_name"]
124
+ username = cred["username"]
125
+ password = cred["password"]
126
+ # uv expects env vars in the form UV_INDEX_{NAME}_USERNAME where
127
+ # {NAME} is the index name uppercased with hyphens replaced by
128
+ # underscores. This is the uv convention for per-index auth.
129
+ # See: https://docs.astral.sh/uv/configuration/indexes/
130
+ upper_name = index_name.upper().replace("-", "_")
131
+ key_user = f"UV_INDEX_{upper_name}_USERNAME"
132
+ key_pass = f"UV_INDEX_{upper_name}_PASSWORD"
133
+ os.environ[key_user] = username
134
+ os.environ[key_pass] = password
135
+ _log(f"Loaded credentials for private repository: {index_name}")
136
+ _log(f"Set environment variable {key_user}")
137
+ _log(f"Set environment variable {key_pass}")
138
+ else:
139
+ _log("No private repository credentials found, using public PyPI only.")
140
+
141
+ # --------------------------------------------------------------------------
142
+ # Step 4 — Run uv sync
143
+ # --------------------------------------------------------------------------
144
+ _log_sub_header("Container Step 4 - Run 'uv sync'")
145
+ st = datetime.now()
146
+ # Flag rationale:
147
+ # --frozen : use the exact versions from uv.lock without
148
+ # re-resolving, ensuring reproducible builds.
149
+ # --no-dev : exclude dev dependencies (pytest, sphinx, etc.)
150
+ # that have no place in a Lambda layer.
151
+ # --no-install-project : skip installing the project package itself;
152
+ # the Lambda layer only needs the *dependencies*.
153
+ # The project code is deployed separately as the
154
+ # Lambda function handler.
155
+ # --link-mode=copy : copy files instead of symlinking. Lambda layers
156
+ # are zipped and uploaded — symlinks would break
157
+ # because the link targets don't exist in the
158
+ # Lambda execution environment.
159
+ subprocess.run(
160
+ [
161
+ str(path_bin_uv),
162
+ "sync",
163
+ "--frozen",
164
+ "--no-dev",
165
+ "--no-install-project",
166
+ "--link-mode=copy",
167
+ ],
168
+ cwd=str(dir_repo),
169
+ check=True,
170
+ )
171
+ elapsed = (datetime.now() - st).total_seconds()
172
+ _log(f"uv sync elapsed: {elapsed:.2f} seconds")
173
+
174
+ major, minor = sys.version_info[:2]
175
+ dir_site_packages = (
176
+ dir_repo / ".venv" / "lib" / f"python{major}.{minor}" / "site-packages"
177
+ )
178
+ _log(f"Dependencies installed at: {dir_site_packages}")
179
+ _log("Container-side build completed successfully!")
180
+
181
+
182
+ if __name__ == "__main__": # pragma: no cover
183
+ main()
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # From this package
4
+ # from .builder import UvLambdaLayerLocalBuilder
5
+ # from .builder import UvLambdaLayerContainerBuilder
6
+ from .local_builder import UvLambdaLayerLocalBuilder
7
+ from .container_builder import UvLambdaLayerContainerBuilder
8
+ from .validate import validate_artifacts
9
+
10
+ # Re-export from core for end-user convenience
11
+ from aws_lbd_art_builder_core.layer.api import Credentials
12
+ from aws_lbd_art_builder_core.layer.api import LayerPathLayout
13
+ from aws_lbd_art_builder_core.layer.api import LayerS3Layout
14
+ from aws_lbd_art_builder_core.layer.api import move_to_dir_python
15
+ from aws_lbd_art_builder_core.layer.api import default_ignore_package_list
16
+ from aws_lbd_art_builder_core.layer.api import create_layer_zip_file
17
+ from aws_lbd_art_builder_core.layer.api import upload_layer_zip_to_s3
18
+ from aws_lbd_art_builder_core.layer.api import LambdaLayerVersionPublisher
19
+ from aws_lbd_art_builder_core.layer.api import LayerDeployment
@@ -0,0 +1,177 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ UV-based containerized Lambda layer builder.
5
+
6
+ :class:`UvLambdaLayerContainerBuilder` orchestrates a Docker-based build
7
+ that runs ``uv sync`` inside an official AWS SAM image.
8
+
9
+ See ``docs/source/99-Maintainer-Guide/03-Build-Lambda-Layer-using-UV-in-Container``
10
+ for the full architecture guide.
11
+ """
12
+
13
+ import subprocess
14
+ import dataclasses
15
+ from pathlib import Path
16
+
17
+ import aws_lbd_art_builder_core.api as aws_lbd_art_builder_core
18
+
19
+ from ..paths import path_enum
20
+
21
+
22
+ @dataclasses.dataclass(frozen=True)
23
+ class UvLambdaLayerContainerBuilder(
24
+ aws_lbd_art_builder_core.layer_api.BaseLambdaLayerContainerBuilder,
25
+ ): # pragma: no cover
26
+ """
27
+ Build a Lambda layer using uv inside a Docker container.
28
+
29
+ **Mount scheme**: ``{project_root}/build/lambda/layer/`` → ``/var/task/``
30
+
31
+ The container script (``_build_in_container.py``) is pure stdlib — no
32
+ third-party packages are installed inside the container except ``uv``.
33
+ """
34
+
35
+ # fmt: off
36
+ path_script: Path = dataclasses.field(default=path_enum.path_build_in_container_script)
37
+ # credentials is optional because most projects only need public PyPI.
38
+ # When private repositories are needed, the caller passes a Credentials
39
+ # object that gets serialized to JSON and read inside the container.
40
+ credentials: aws_lbd_art_builder_core.layer_api.Credentials | None = dataclasses.field(default=None)
41
+ skip_prompt: bool = dataclasses.field(default=False)
42
+ # fmt: on
43
+
44
+ def run(self):
45
+ self.step_1_preflight_check()
46
+ self.step_2_prepare_environment()
47
+ self.step_3_execute_build()
48
+ self.step_4_finalize_artifacts()
49
+
50
+ # --- Step 1 ---------------------------------------------------------------
51
+ def step_1_preflight_check(self):
52
+ self.log_header("Step 1 - Preflight Check")
53
+ self.step_1_1_print_info()
54
+ self.step_1_2_check()
55
+
56
+ def step_1_1_print_info(self):
57
+ # fmt: off
58
+ self.log_sub_header("Step 1.1 - Print Build Info")
59
+ self.log_detail(f"path_pyproject_toml = {self.path_pyproject_toml}")
60
+ self.log_detail(f"py_ver_major = {self.py_ver_major}")
61
+ self.log_detail(f"py_ver_minor = {self.py_ver_minor}")
62
+ self.log_detail(f"is_arm = {self.is_arm}")
63
+ self.log_detail(f"path_script = {self.path_script}")
64
+ self.log_detail(f"dir_repo = {self.path_layout.dir_repo}")
65
+ self.log_detail(f"dir_build_layer = {self.path_layout.dir_build_lambda_layer}")
66
+ # fmt: on
67
+
68
+ def step_1_2_check(self):
69
+ """
70
+ Check that ``uv.lock`` exists before starting the build.
71
+
72
+ Container builds are slow and resource-heavy (pulling Docker images,
73
+ spinning up a container). Failing early on a missing lock file
74
+ saves minutes of wasted time. The local builder skips this check
75
+ because local builds are fast enough that discovering the missing
76
+ lock file at ``uv sync`` time is acceptable.
77
+ """
78
+ self.log_sub_header("Step 1.2 - Check")
79
+ path_uv_lock = self.path_layout.dir_project_root / "uv.lock"
80
+ self.log_detail(f"Check if '{path_uv_lock}' exists ...")
81
+ if path_uv_lock.exists():
82
+ self.log_detail("Exists!")
83
+ else:
84
+ raise FileNotFoundError(
85
+ f"UV lock file not found: {path_uv_lock}, "
86
+ f"cannot proceed with uv-based build. "
87
+ f"Please run 'uv lock' to generate the lock file."
88
+ )
89
+
90
+ # --- Step 2 ---------------------------------------------------------------
91
+ def step_2_prepare_environment(self):
92
+ self.log_header("Step 2 - Prepare Environment")
93
+ self.step_2_1_setup_build_dir()
94
+ self.step_2_2_copy_build_script()
95
+ self.step_2_3_setup_private_repository_credential()
96
+ self.step_2_4_prepare_uv_stuff()
97
+
98
+ def step_2_1_setup_build_dir(self):
99
+ """
100
+ Delete ``build/lambda/layer/`` and recreate the directory structure.
101
+ """
102
+ self.log_sub_header("Step 2.1 - Setup Build Directory")
103
+ dir = self.path_layout.dir_build_lambda_layer
104
+ self.log_detail(f"Clean existing build directory: {dir}")
105
+ self.path_layout.clean(skip_prompt=self.skip_prompt)
106
+ self.path_layout.mkdirs()
107
+
108
+ def step_2_2_copy_build_script(self):
109
+ """
110
+ Copy ``_build_in_container.py`` →
111
+ ``build/lambda/layer/build_lambda_layer_in_container.py``.
112
+ """
113
+ self.log_sub_header("Step 2.2 - Copy Build Script")
114
+ p_dst = self.path_layout.path_build_lambda_layer_in_container_script_in_local
115
+ self.path_layout.copy_file(
116
+ p_src=self.path_script,
117
+ p_dst=p_dst,
118
+ printer=self.log_detail,
119
+ )
120
+
121
+ def step_2_3_setup_private_repository_credential(self):
122
+ """
123
+ Dump credentials JSON to ``build/lambda/layer/private-repository-credentials.json``
124
+ for the container script to read.
125
+ """
126
+ self.log_sub_header("Step 2.3 - Setup Private Repository Credential")
127
+ if not isinstance(
128
+ self.credentials, aws_lbd_art_builder_core.layer_api.Credentials
129
+ ):
130
+ self.log_detail("No private repository credentials provided, skip.")
131
+ return
132
+ p = (
133
+ self.path_layout.dir_build_lambda_layer
134
+ / "private-repository-credentials.json"
135
+ )
136
+ self.log_detail(f"Dump private repository credentials to {p}")
137
+ self.credentials.dump(path=p)
138
+
139
+ def step_2_4_prepare_uv_stuff(self):
140
+ """
141
+ Copy ``pyproject.toml`` and ``uv.lock`` to ``build/lambda/layer/repo/``.
142
+ """
143
+ self.log_sub_header("Step 2.4 - Prepare UV stuff")
144
+ self.path_layout.copy_pyproject_toml(printer=self.log_detail)
145
+ self.path_layout.copy_file(
146
+ p_src=self.path_layout.dir_project_root / "uv.lock",
147
+ p_dst=self.path_layout.dir_repo / "uv.lock",
148
+ printer=self.log_detail,
149
+ )
150
+
151
+ # --- Step 3 ---------------------------------------------------------------
152
+ def step_3_execute_build(self):
153
+ self.log_header("Step 3 - Execute Build")
154
+ self.step_3_1_docker_run()
155
+
156
+ def step_3_1_docker_run(self):
157
+ self.log_sub_header("Step 3.1 - Docker Run")
158
+ subprocess.run(self.docker_run_args, check=True)
159
+
160
+ # --- Step 4 ---------------------------------------------------------------
161
+ def step_4_finalize_artifacts(self):
162
+ """
163
+ Move ``repo/.venv/lib/pythonX.Y/site-packages/`` →
164
+ ``artifacts/python/`` on the host.
165
+ """
166
+ self.log_header("Step 4 - Finalize Artifacts")
167
+ self.step_4_1_move_site_packages_to_python()
168
+
169
+ def step_4_1_move_site_packages_to_python(self):
170
+ self.log_sub_header("Step 4.1 - Move site-packages to python/")
171
+ dir_source = self.path_layout.dir_build_lambda_layer_repo_venv_site_packages
172
+ dir_target = self.path_layout.dir_python
173
+ self.log_detail(f"Move '{dir_source}' to '{dir_target}'")
174
+ aws_lbd_art_builder_core.layer_api.move_to_dir_python(
175
+ dir_site_packages=dir_source,
176
+ dir_python=dir_target,
177
+ )