Skip to content

Commit e6b2caf

Browse files
committed
chore: use per-synth builder containers
1 parent 56458b5 commit e6b2caf

16 files changed

Lines changed: 990 additions & 422 deletions

.github/copilot-instructions.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# uv-python-lambda Copilot Instructions
2+
3+
## Build, test, and lint
4+
5+
- Install dependencies with `yarn install --check-files` (matches CI).
6+
- Build with `npx projen build`.
7+
- Run the full test suite with `npx projen test`.
8+
- Run a single test file with `npx jest test/function.test.ts`.
9+
- Run a single test case with `npx jest test/function.test.ts -t "Create a function from basic_app"`.
10+
- Lint/format checks use Biome: `npx biome check`.
11+
- To check a specific file with Biome, run `npx biome check src/function.ts`.
12+
13+
## High-level architecture
14+
15+
- This repository is a **projen-managed jsii CDK construct library**. The human-authored sources are mainly in `src/`, `test/`, `resources/`, and `.projenrc.ts`. Generated project files such as `package.json`, GitHub workflows, and packaging config should be changed via `.projenrc.ts` and then regenerated with `npx projen`.
16+
- The public API is exported from `src/index.ts`. The main construct is `PythonFunction` in `src/function.ts`, which extends `aws-cdk-lib/aws-lambda.Function`.
17+
- `PythonFunction` is a thin orchestration layer: it resolves the Lambda handler name from `index` + `handler`, enforces a Python runtime, defaults to `Architecture.ARM_64` and `Runtime.PYTHON_3_12`, and delegates packaging to `Bundling.bundle(...)`.
18+
- `src/bundling.ts` owns packaging. It creates a Docker builder image from `resources/`, caches long-lived builder containers by a hash of runtime/architecture/build args/root dir, and returns Lambda code through `Code.fromCustomCommand(...)` so CDK can stage the packaged asset.
19+
- The Docker-side build flow lives in `resources/entrypoint.sh` and `resources/export.sh`:
20+
- `entrypoint.sh` starts the builder container, mounts an overlay filesystem over `/src`, creates a uv virtualenv, runs `uv sync --no-dev --frozen --no-editable`, touches a lock file, and then stays alive.
21+
- `export.sh` runs later via `docker exec`; it waits for that lock file, optionally scopes to `--package <workspace>`, exports requirements, and installs dependencies into the requested asset output directory.
22+
- `workspacePackage` is the key monorepo/workspace feature: it tells bundling to treat a specific uv workspace package as the Lambda entry package while still resolving dependencies from the workspace root.
23+
- Tests in `test/function.test.ts` are integration-style CDK packaging tests, not isolated unit tests. They create real CDK apps/stacks, require Docker, use fixture uv projects from `test/resources/`, and inspect the staged asset contents through the custom metadata key `uv-python-lambda:asset-path`.
24+
25+
## Key conventions
26+
27+
- **Do not hand-edit generated project files.** `package.json` explicitly says it is generated by projen, and the workflows under `.github/workflows/` are generated from `.projenrc.ts`.
28+
- **Keep changes aligned with Biome formatting/import ordering.** The repo uses Biome instead of ESLint/Prettier, with single quotes and organize-imports enabled.
29+
- **Preserve jsii-friendly public types.** Public configuration is expressed as exported TypeScript interfaces (`PythonFunctionProps`, `BundlingOptions`, `ICommandHooks`) because this library is packaged for TypeScript and Python consumers.
30+
- **Bundling assumes Docker-based execution.** There is no parallel local packager path; changes to packaging behavior usually involve `src/bundling.ts` plus the shell scripts in `resources/`.
31+
- **Be careful with architecture defaults.** Production code defaults Lambda functions to ARM64, while tests intentionally detect the Docker host architecture to avoid slow QEMU emulation on GitHub runners.
32+
- **Asset metadata is intentional.** `PythonFunction` writes `uv-python-lambda:asset-path` metadata so tests and downstream CDK asset inspection can find the staged bundle.

.projen/deps.json

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projen/tasks.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projenrc.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,20 @@ const project = new awscdk.AwsCdkConstructLibrary({
2121
},
2222
// cdkVersion: '2.1.0', /* CDK version to use. */
2323
// cdkDependencies: [], /* CDK dependencies of this module. */
24-
bundledDeps: ['object-hash', 'atomic-sleep'], /* Dependencies which should be bundled in the package. */
24+
bundledDeps: [
25+
'object-hash',
26+
] /* Dependencies which should be bundled in the package. */,
2527
// deps: [], /* Runtime dependencies of this module. */
2628
// description: undefined, /* The description is just a string that helps people understand the purpose of the package. */
27-
devDeps: ['@biomejs/biome', '@types/object-hash', '@types/atomic-sleep'] /* Build dependencies for this module. */,
29+
devDeps: [
30+
'@biomejs/biome',
31+
'@types/object-hash',
32+
] /* Build dependencies for this module. */,
2833
// packageName: undefined, /* The "name" in package.json. */
2934
jestOptions: {
3035
extraCliOptions: ['--testTimeout=300000'],
3136
},
32-
gitignore: [
33-
".vscode/",
34-
],
37+
gitignore: ['.vscode/'],
3538
eslint: false,
3639
});
3740

API.md

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ FROM ${BUNDLING_IMAGE}
77
# FROM public.ecr.aws/lambda/python:3.12-$IMAGE_ARCH
88
COPY --from=uv /uv /uvx /bin/
99
COPY *.sh /root
10+
RUN chmod +x /root/*.sh
1011
WORKDIR /root
11-
ENTRYPOINT [ "/root/entrypoint.sh" ]
12+
ENTRYPOINT [ "/root/entrypoint.sh" ]

resources/entrypoint.sh

100755100644
Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,24 @@
11
#!/bin/bash
22

3-
# /uvbuild is the working directory for things like cache and staged outputs
4-
# /src is the project root, where the top-level pyproject.toml/uv.lock exists
3+
# /uvbuild is the working directory for caches and staged outputs.
4+
# /src is the project root mounted from the host.
55

66
set -euo pipefail
77

88
export LOCK_FILE=/uvbuild/uv-python-lambda.lock
9+
export UV_LINK_MODE=copy
10+
export UV_NO_INSTALLER_METADATA=1
911

10-
rm -f $LOCK_FILE
12+
rm -f "$LOCK_FILE"
1113

12-
# Changing the default UV_LINK_MODE silences warnings about not being able to use hard links
13-
# since the cache and sync target may be on separate file systems.
14-
export UV_LINK_MODE=copy
1514
mkdir -p /uvbuild/uvcache
1615
mkdir -p /root/.cache
1716
ln -sf /uvbuild/uvcache /root/.cache/uv
1817

19-
# Set up overlay filesystem
20-
mkdir -p /tmp/overlay
21-
mount -t tmpfs tmpfs /tmp/overlay
22-
mkdir -p /tmp/overlay/{upper,work,merged}
23-
mount -t overlay overlay -o rw,lowerdir=/src,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work /tmp/overlay/merged
24-
25-
echo Overlay has been set up
26-
27-
cd /tmp/overlay/merged
28-
rm -rf .venv # Remove (hide) any host system venv from the overlay filesystem
29-
30-
uv venv --python-preference=only-system
31-
uv sync --compile-bytecode --no-dev --frozen --no-editable
32-
33-
touch $LOCK_FILE
18+
touch "$LOCK_FILE"
3419

3520
echo Builder container is ready and waiting
21+
3622
while true; do
3723
sleep 1
38-
done
24+
done

0 commit comments

Comments
 (0)