diff --git a/pyproject.toml b/pyproject.toml index 3430ccae..90f89275 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ dependencies = [ # SymbolTable.expr_host_rules, and `let` on StepTemplate/StepScript. 0.11.0 has # only StepTemplate.let, StepScript.let and SymbolTable.expr_types, so this # package fails at import against it. - "openjd-model >= 0.11.1,< 0.12", + "openjd-model >= 0.11.2,< 0.12", "pywin32 >= 307; platform_system == 'Windows'", "psutil >= 5.9,< 7.3; platform_system == 'Windows'", ] diff --git a/src/openjd/sessions/_v1/_session.py b/src/openjd/sessions/_v1/_session.py index fb1aa563..db9fbe01 100644 --- a/src/openjd/sessions/_v1/_session.py +++ b/src/openjd/sessions/_v1/_session.py @@ -299,6 +299,7 @@ def run_task( os_env_vars: Optional[dict[str, str]] = None, log_task_banner: bool = True, resolved_symtab: Optional[SerializedSymbolTable] = None, + step_name: Optional[str] = None, ) -> None: # ``resolved_symtab`` is the step-scope symbol table generated # by ``create_job`` (available as ``Step.resolved_symtab``). @@ -310,6 +311,9 @@ def run_task( # bindings and no expression interpolation that depends on # step-scope state. # + # ``step_name`` is surfaced as ``WrappedStep.Name`` to a wrapping + # environment's ``onWrapTaskRun`` hook (RFC 0008). + # # ``log_task_banner`` is accepted for API compatibility but # currently has no effect — the Rust runner always emits the # task banner. TODO: plumb through if/when the Rust API @@ -319,6 +323,7 @@ def run_task( task_parameter_values=task_parameter_values, resolved_symtab=resolved_symtab, os_env_vars=os_env_vars, + step_name=step_name, ) self._fire_initial_running_callback() self._poll_for_completion() diff --git a/test/openjd/sessions_v1/test_wrap_task_run.py b/test/openjd/sessions_v1/test_wrap_task_run.py new file mode 100644 index 00000000..d39dd7ad --- /dev/null +++ b/test/openjd/sessions_v1/test_wrap_task_run.py @@ -0,0 +1,107 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +"""Tests that the _v1 Session.run_task accepts and forwards step_name +to the Rust binding without error (RFC 0008). + +The full wrap-action integration (``{{WrappedStep.Name}}`` resolution inside an +``onWrapTaskRun`` hook) is validated by the worker-agent's differential runtime +test. This test verifies the _v1 Python wrapper plumbs the kwarg through. +""" + +from __future__ import annotations + +import time +import uuid +from pathlib import Path + +import pytest + +from openjd.model._v1 import create_job, decode_job_template +from openjd.sessions._v1 import Session, SessionState, ActionState + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +_SIMPLE_TEMPLATE = { + "specificationVersion": "jobtemplate-2023-09", + "name": "StepNameTest", + "steps": [ + { + "name": "MyStep", + "script": { + "actions": { + "onRun": {"command": "echo", "args": ["hello-from-task"]}, + } + }, + } + ], +} + + +def _run_until_ready(session: Session, timeout_s: float = 10.0) -> None: + deadline = time.time() + timeout_s + while session.state == SessionState.RUNNING and time.time() < deadline: + time.sleep(0.05) + + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- + + +class TestV1RunTaskStepName: + """Verify that _v1 Session.run_task forwards step_name to the Rust binding.""" + + def test_run_task_accepts_step_name( + self, tmp_path: Path, caplog: pytest.LogCaptureFixture + ) -> None: + """run_task(step_name=...) succeeds and the task completes normally.""" + job_template = decode_job_template(template=_SIMPLE_TEMPLATE) + job = create_job(job_template=job_template, job_parameter_values={}) + step = job.steps[0] + + with Session( + session_id=uuid.uuid4().hex, + job_parameter_values={}, + session_root_directory=tmp_path, + ) as session: + session.run_task( + step_script=step.script, + task_parameter_values={}, + resolved_symtab=step.resolved_symtab, + step_name="MyStep", + ) + _run_until_ready(session) + + assert session.state == SessionState.READY + assert session.action_status is not None + assert session.action_status.state == ActionState.SUCCESS + assert session.action_status.exit_code == 0 + + messages = "\n".join(caplog.messages) + assert "hello-from-task" in messages + + def test_run_task_step_name_none_also_works(self, tmp_path: Path) -> None: + """step_name=None (the default) still works.""" + job_template = decode_job_template(template=_SIMPLE_TEMPLATE) + job = create_job(job_template=job_template, job_parameter_values={}) + step = job.steps[0] + + with Session( + session_id=uuid.uuid4().hex, + job_parameter_values={}, + session_root_directory=tmp_path, + ) as session: + session.run_task( + step_script=step.script, + task_parameter_values={}, + resolved_symtab=step.resolved_symtab, + # step_name defaults to None + ) + _run_until_ready(session) + + assert session.state == SessionState.READY + assert session.action_status is not None + assert session.action_status.state == ActionState.SUCCESS + assert session.action_status.exit_code == 0