Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "truss"
version = "0.11.12rc0"
version = "0.11.13rc1"
description = "A seamless bridge from model development to model delivery"
authors = [
{ name = "Pankaj Gupta", email = "[email protected]" },
Expand Down
12 changes: 6 additions & 6 deletions truss/templates/server.Dockerfile.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ RUN mkdir -p {{ dst.parent }}; curl -L "{{ url }}" -o {{ dst }}
{% endfor %} {#- endfor external_data_files #}
{%- endif %} {#- endif external_data_files #}

{%- if build_commands %}
{% for command in build_commands %}
RUN {% for secret,path in config.build.secret_to_path_mapping.items() %} --mount=type=secret,id={{ secret }},target={{ path }}{%- endfor %} {{ command }}
{% endfor %} {#- endfor build_commands #}
{%- endif %} {#- endif build_commands #}

{# Copy data before code for better caching #}
{%- if data_dir_exists %}
COPY --chown={{ default_owner }} ./{{ config.data_dir }} ${APP_HOME}/data
Expand Down Expand Up @@ -101,6 +95,12 @@ COPY --chown={{ default_owner }} ./{{ config.model_module_dir }} ${APP_HOME}/mod
{% endblock %} {#- endblock app_copy #}

{% block run %}
{%- if build_commands %}
{% for command in build_commands %}
RUN {% for secret,path in config.build.secret_to_path_mapping.items() %} --mount=type=secret,id={{ secret }},target={{ path }}{%- endfor %} {{ command }}
{% endfor %} {#- endfor build_commands #}
{%- endif %} {#- endif build_commands #}

{# Macro to change ownership of directories and switch to regular user #}
{%- macro chown_and_switch_to_regular_user_if_enabled(additional_chown_dirs=[]) -%}
{%- if non_root_user %}
Expand Down
9 changes: 9 additions & 0 deletions truss/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,15 @@ def helpers():
return Helpers()


@pytest.fixture
def test_editable_external_pkg(test_data_path, tmp_path):
truss_dir = test_data_path / "test_editable_external_pkg"
parent_dir = test_data_path / "test_editable_external_pkg_parent"
shutil.copytree(truss_dir, tmp_path / "test_editable_external_pkg")
shutil.copytree(parent_dir, tmp_path / "test_editable_external_pkg_parent")
return tmp_path / "test_editable_external_pkg"


def _build_truss_fs(truss_dir: Path, tmp_path: Path) -> Path:
truss_fs = tmp_path / "truss_fs"
truss_fs.mkdir()
Expand Down
5 changes: 5 additions & 0 deletions truss/tests/test_data/test_editable_external_pkg/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
external_package_dirs:
- ../test_editable_external_pkg_parent

build_commands:
- uv pip install --system -e /packages/local_pkg
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import local_pkg


class Model:
def predict(self, request):
return {"predictions": [local_pkg.VALUE]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "local-pkg"
version = "0.0.1"

[tool.hatch.build.targets.wheel]
packages = ["src/local_pkg"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VALUE = 42
42 changes: 42 additions & 0 deletions truss/tests/test_truss_handle.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from truss.base.custom_types import Example
from truss.base.errors import ContainerIsDownError, ContainerNotFoundError
from truss.base.truss_config import map_local_to_supported_python_version
from truss.contexts.image_builder.serving_image_builder import (
ServingImageBuilderContext,
)
from truss.contexts.image_builder.util import TRUSS_BASE_IMAGE_VERSION_TAG
from truss.local.local_config_handler import LocalConfigHandler
from truss.templates.control.control.helpers.custom_types import (
Expand Down Expand Up @@ -480,6 +483,27 @@ def test_build_commands(test_data_path):
assert r1 == {"predictions": [1, 2]}


def test_build_commands_in_run_block_order(test_data_path, tmp_path):
truss_dir = test_data_path / "test_editable_external_pkg"
build_dir = tmp_path / "build_dir"
image_builder = ServingImageBuilderContext.run(truss_dir)
image_builder.prepare_image_build_dir(build_dir)
dockerfile = (build_dir / "Dockerfile").read_text()

cmd_idx = dockerfile.find("uv pip install --system -e /packages/local_pkg")
if cmd_idx == -1:
cmd_idx = dockerfile.find("uv pip install -e /packages/local_pkg")
if cmd_idx == -1:
cmd_idx = dockerfile.find("pip install -e /packages/local_pkg")
assert cmd_idx != -1

entry_idx = dockerfile.find("ENTRYPOINT ")
user_idx = dockerfile.find("\nUSER ")
if user_idx != -1:
assert cmd_idx < user_idx
assert cmd_idx < entry_idx


@pytest.mark.integration
def test_build_commands_failure(test_data_path):
truss_dir = test_data_path / "test_build_commands_failure"
Expand Down Expand Up @@ -848,3 +872,21 @@ def test_config_verbose(custom_model_truss_dir_with_pre_and_post):
th.live_reload()
new_config["live_reload"] = True
assert new_config == th.spec.config.to_dict(verbose=False)


@pytest.mark.integration
def test_editable_external_package_install_and_predict(test_editable_external_pkg):
th = TrussHandle(test_editable_external_pkg)
# Ensure standard inference server flow for this test
th.live_reload(False)
tag = "test-editable-ext-pkg:0.0.1"
with ensure_kill_all():
container = th.docker_run(tag=tag, local_port=None)
try:
verify_python_requirement_installed_on_container(container, "local-pkg")
finally:
Docker.client().kill(container)

with ensure_kill_all():
result = th.docker_predict([1], tag=tag, local_port=None)
assert result == {"predictions": [42]}