diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ff11ad..243ec52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Editing an external workflow creates a local copy to shadow it. - Deleting an external workflow without a local shadow fails. - Re-discovering does not override shadows. + - Saving python environment requirements in discovered workflows is opt-in. ## [2.1.2] - 2026-03-06 diff --git a/src/ewoksserver/app/models.py b/src/ewoksserver/app/models.py index b72912d..aab80e4 100644 --- a/src/ewoksserver/app/models.py +++ b/src/ewoksserver/app/models.py @@ -25,6 +25,10 @@ class EwoksDiscoverySettings(BaseModel): default=True, title="Create a local copy of a workflow when it is discovered", ) + save_requirements: bool = Field( + default=False, + title="Save the python environment requirements in external workflows", + ) class EwoksExecutionSettings(BaseModel): diff --git a/src/ewoksserver/app/routes/workflows/backend.py b/src/ewoksserver/app/routes/workflows/backend.py index 3659faf..bf0e9f3 100644 --- a/src/ewoksserver/app/routes/workflows/backend.py +++ b/src/ewoksserver/app/routes/workflows/backend.py @@ -202,7 +202,8 @@ def _load_external_workflow( kwargs = dict(worker_options) kwargs["args"] = (identifier, None) kwargs["kwargs"] = { - "load_options": {"representation": "json_module", "root_module": package} + "load_options": {"representation": "json_module", "root_module": package}, + "save_requirements": settings.ewoks_discovery.save_requirements, } timeout = settings.ewoks_discovery.timeout diff --git a/src/ewoksserver/tests/conftest.py b/src/ewoksserver/tests/conftest.py index 093192b..abc5b37 100644 --- a/src/ewoksserver/tests/conftest.py +++ b/src/ewoksserver/tests/conftest.py @@ -74,6 +74,31 @@ def get_ewoks_settings_for_tests(): yield client +@pytest.fixture +def rest_client_save_requirements(tmp_path): + """Client to the REST server (no execution) which saves the python + environment requirements in discovered workflows.""" + app = newserver.create_app() + + @lru_cache() + def get_ewoks_settings_for_tests(): + return serverconfig.EwoksSettings( + configured=True, + resource_directory=str(tmp_path), + # Disable discovery since this client is used to test manual discovery + ewoks_discovery=EwoksDiscoverySettings( + on_start_up=False, save_requirements=True + ), + ) + + app.dependency_overrides[serverconfig.get_ewoks_settings] = ( + get_ewoks_settings_for_tests + ) + + with TestClient(app) as client: + yield client + + @pytest.fixture def local_patched_ewoks_worker(monkeypatch): """Only works for local (in-process) ewoksjob worker.""" diff --git a/src/ewoksserver/tests/test_workflow_discover.py b/src/ewoksserver/tests/test_workflow_discover.py index c7f0037..7945a96 100644 --- a/src/ewoksserver/tests/test_workflow_discover.py +++ b/src/ewoksserver/tests/test_workflow_discover.py @@ -151,6 +151,36 @@ def test_no_cache_on_discovery(rest_client_no_discover_cache, api_root, tmp_path assert not _workflow_file(tmp_path, "ewoksserver.tests._loadtest.graph").exists() +@api_version_bounds(min_version="2.1.0") +def test_no_requirements_on_discovery(rest_client, api_root): + response = rest_client.post( + f"{api_root}/workflows/discover", + json={"modules": ["ewoksserver.tests._loadtest.*"]}, + ) + assert response.status_code == 200, response.json() + + identifier = "ewoksserver.tests._loadtest.subgraph" + response = rest_client.get(f"{api_root}/workflow/{identifier}") + data = response.json() + assert response.status_code == 200, data + assert not data["graph"].get("requirements") + + +@api_version_bounds(min_version="2.1.0") +def test_requirements_on_discovery(rest_client_save_requirements, api_root): + response = rest_client_save_requirements.post( + f"{api_root}/workflows/discover", + json={"modules": ["ewoksserver.tests._loadtest.*"]}, + ) + assert response.status_code == 200, response.json() + + identifier = "ewoksserver.tests._loadtest.subgraph" + response = rest_client_save_requirements.get(f"{api_root}/workflow/{identifier}") + data = response.json() + assert response.status_code == 200, data + assert data["graph"]["requirements"] + + @api_version_bounds(min_version="2.1.0") def test_discover_does_not_override_local_copy(rest_client, api_root, tmp_path): identifier = "ewoksserver.tests._loadtest.subgraph"