|
12 | 12 | import click |
13 | 13 |
|
14 | 14 | from flyte import Secret |
| 15 | +from flyte._code_bundle._ignore import STANDARD_IGNORE_PATTERNS |
15 | 16 | from flyte._image import ( |
16 | 17 | AptPackages, |
17 | 18 | Commands, |
|
38 | 39 | LocalDockerCommandImageChecker, |
39 | 40 | LocalPodmanCommandImageChecker, |
40 | 41 | ) |
41 | | -from flyte._internal.imagebuild.utils import copy_files_to_context, get_and_list_dockerignore |
| 42 | +from flyte._internal.imagebuild.utils import ( |
| 43 | + copy_files_to_context, |
| 44 | + get_and_list_dockerignore, |
| 45 | + get_uv_editable_install_mounts, |
| 46 | +) |
42 | 47 | from flyte._logging import logger |
43 | 48 |
|
44 | 49 | _F_IMG_ID = "_F_IMG_ID" |
|
49 | 54 | RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \ |
50 | 55 | --mount=type=bind,target=uv.lock,src=$UV_LOCK_PATH,rw \ |
51 | 56 | --mount=type=bind,target=pyproject.toml,src=$PYPROJECT_PATH \ |
| 57 | + $EDITABLE_INSTALL_MOUNTS \ |
52 | 58 | $SECRET_MOUNT \ |
53 | 59 | VIRTUAL_ENV=$${VIRTUAL_ENV-/opt/venv} uv sync --active --inexact $PIP_INSTALL_ARGS |
54 | 60 | """) |
@@ -271,16 +277,24 @@ async def handle( |
271 | 277 | pip_install_args = " ".join(layer.get_pip_install_args()) |
272 | 278 | if "--no-install-project" not in pip_install_args: |
273 | 279 | pip_install_args += " --no-install-project" |
274 | | - if "--no-sources" not in pip_install_args: |
275 | | - pip_install_args += " --no-sources" |
276 | | - # Only Copy pyproject.yaml and uv.lock. |
| 280 | + # Only Copy pyproject.yaml and uv.lock from the project root. |
277 | 281 | pyproject_dst = copy_files_to_context(layer.pyproject, context_path) |
278 | 282 | uvlock_dst = copy_files_to_context(layer.uvlock, context_path) |
| 283 | + # Apply any editable install mounts to the template. |
| 284 | + editable_install_mounts = get_uv_editable_install_mounts( |
| 285 | + project_root=layer.pyproject.parent, |
| 286 | + context_path=context_path, |
| 287 | + ignore_patterns=[ |
| 288 | + *STANDARD_IGNORE_PATTERNS, |
| 289 | + *docker_ignore_patterns, |
| 290 | + ], |
| 291 | + ) |
279 | 292 | delta = UV_LOCK_WITHOUT_PROJECT_INSTALL_TEMPLATE.substitute( |
280 | 293 | UV_LOCK_PATH=uvlock_dst.relative_to(context_path), |
281 | 294 | PYPROJECT_PATH=pyproject_dst.relative_to(context_path), |
282 | 295 | PIP_INSTALL_ARGS=pip_install_args, |
283 | 296 | SECRET_MOUNT=secret_mounts, |
| 297 | + EDITABLE_INSTALL_MOUNTS=editable_install_mounts, |
284 | 298 | ) |
285 | 299 | else: |
286 | 300 | # Copy the entire project. |
@@ -429,19 +443,19 @@ def _get_secret_command(secret: str | Secret) -> typing.List[str]: |
429 | 443 | def _get_secret_mounts_layer(secrets: typing.Tuple[str | Secret, ...] | None) -> str: |
430 | 444 | if secrets is None: |
431 | 445 | return "" |
432 | | - secret_mounts_layer = "" |
| 446 | + secret_mounts_layer = [] |
433 | 447 | for s in secrets: |
434 | 448 | secret = Secret(key=s) if isinstance(s, str) else s |
435 | 449 | secret_id = hash(secret) |
436 | 450 | if secret.mount: |
437 | | - secret_mounts_layer += f"--mount=type=secret,id={secret_id},target={secret.mount}" |
| 451 | + secret_mounts_layer.append(f"--mount=type=secret,id={secret_id},target={secret.mount}") |
438 | 452 | elif secret.as_env_var: |
439 | | - secret_mounts_layer += f"--mount=type=secret,id={secret_id},env={secret.as_env_var}" |
| 453 | + secret_mounts_layer.append(f"--mount=type=secret,id={secret_id},env={secret.as_env_var}") |
440 | 454 | else: |
441 | 455 | secret_default_env_key = "_".join(list(filter(None, (secret.group, secret.key)))) |
442 | | - secret_mounts_layer += f"--mount=type=secret,id={secret_id},env={secret_default_env_key}" |
| 456 | + secret_mounts_layer.append(f"--mount=type=secret,id={secret_id},env={secret_default_env_key}") |
443 | 457 |
|
444 | | - return secret_mounts_layer |
| 458 | + return " ".join(secret_mounts_layer) |
445 | 459 |
|
446 | 460 |
|
447 | 461 | async def _process_layer( |
|
0 commit comments