diff --git a/pyproject.toml b/pyproject.toml index 59afcac..120521c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,8 +29,8 @@ classifiers = [ "Intended Audience :: End Users/Desktop" ] dependencies = [ - "openjd-sessions >= 0.10.3,< 0.11", - "openjd-model >= 0.8,< 0.9" + "openjd-sessions >= 0.10.7,< 0.11", + "openjd-model >= 0.9,< 0.10" ] [project.urls] diff --git a/src/openjd/cli/_common/_extensions.py b/src/openjd/cli/_common/_extensions.py index 02fee28..3732d3b 100644 --- a/src/openjd/cli/_common/_extensions.py +++ b/src/openjd/cli/_common/_extensions.py @@ -4,7 +4,7 @@ from typing import Optional # This is the list of Open Job Description extensions with implemented support -SUPPORTED_EXTENSIONS = ["TASK_CHUNKING", "REDACTED_ENV_VARS"] +SUPPORTED_EXTENSIONS = ["TASK_CHUNKING", "REDACTED_ENV_VARS", "FEATURE_BUNDLE_1"] def add_extensions_argument(run_parser: ArgumentParser): diff --git a/test/openjd/cli/templates/feature_bundle_1_amount_minmax.yaml b/test/openjd/cli/templates/feature_bundle_1_amount_minmax.yaml new file mode 100644 index 0000000..2625c8d --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_amount_minmax.yaml @@ -0,0 +1,23 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 Amount Min Max +extensions: + - FEATURE_BUNDLE_1 +parameterDefinitions: + - name: CpuMin + type: INT + default: 1 + - name: CpuMax + type: INT + default: 4 +steps: + - name: TestStep + hostRequirements: + amounts: + - name: amount.worker.vcpu + min: "{{Param.CpuMin}}" + max: "{{Param.CpuMax}}" + script: + actions: + onRun: + command: bash + args: ["-c", "echo 'Amount min/max works!'"] diff --git a/test/openjd/cli/templates/feature_bundle_1_bash.yaml b/test/openjd/cli/templates/feature_bundle_1_bash.yaml new file mode 100644 index 0000000..107625f --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_bash.yaml @@ -0,0 +1,9 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 Bash Syntax Sugar +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: BashStep + bash: + script: | + echo "Hello from Bash!" diff --git a/test/openjd/cli/templates/feature_bundle_1_cmd.yaml b/test/openjd/cli/templates/feature_bundle_1_cmd.yaml new file mode 100644 index 0000000..d7ffaff --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_cmd.yaml @@ -0,0 +1,9 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 Cmd Syntax Sugar +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: CmdStep + cmd: + script: | + echo Hello from Cmd! diff --git a/test/openjd/cli/templates/feature_bundle_1_eol_auto.yaml b/test/openjd/cli/templates/feature_bundle_1_eol_auto.yaml new file mode 100644 index 0000000..66023e2 --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_eol_auto.yaml @@ -0,0 +1,17 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 EndOfLine AUTO +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: EOLStep + script: + embeddedFiles: + - name: TestFile + type: TEXT + filename: test_eol.txt + data: "line1\nline2\nline3" + endOfLine: AUTO + actions: + onRun: + command: bash + args: ["-c", "cat '{{Task.File.TestFile}}' | xxd"] diff --git a/test/openjd/cli/templates/feature_bundle_1_eol_crlf.yaml b/test/openjd/cli/templates/feature_bundle_1_eol_crlf.yaml new file mode 100644 index 0000000..87dc1f8 --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_eol_crlf.yaml @@ -0,0 +1,17 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 EndOfLine CRLF +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: EOLStep + script: + embeddedFiles: + - name: TestFile + type: TEXT + filename: test_eol.txt + data: "line1\nline2\nline3" + endOfLine: CRLF + actions: + onRun: + command: bash + args: ["-c", "cat '{{Task.File.TestFile}}' | xxd"] diff --git a/test/openjd/cli/templates/feature_bundle_1_eol_lf.yaml b/test/openjd/cli/templates/feature_bundle_1_eol_lf.yaml new file mode 100644 index 0000000..dcb7072 --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_eol_lf.yaml @@ -0,0 +1,17 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 EndOfLine LF +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: EOLStep + script: + embeddedFiles: + - name: TestFile + type: TEXT + filename: test_eol.txt + data: "line1\nline2\nline3" + endOfLine: LF + actions: + onRun: + command: bash + args: ["-c", "cat '{{Task.File.TestFile}}' | xxd"] diff --git a/test/openjd/cli/templates/feature_bundle_1_long_name.yaml b/test/openjd/cli/templates/feature_bundle_1_long_name.yaml new file mode 100644 index 0000000..2a32ada --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_long_name.yaml @@ -0,0 +1,11 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 Extended Step Name +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: ThisIsAVeryLongStepNameThatExceedsTheSixtyFourCharacterLimitButIsAllowedWithFeatureBundle1Extension + script: + actions: + onRun: + command: bash + args: ["-c", "echo 'Long step name works!'"] diff --git a/test/openjd/cli/templates/feature_bundle_1_notify_period.yaml b/test/openjd/cli/templates/feature_bundle_1_notify_period.yaml new file mode 100644 index 0000000..f00b4f5 --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_notify_period.yaml @@ -0,0 +1,18 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 Notify Period +extensions: + - FEATURE_BUNDLE_1 +parameterDefinitions: + - name: NotifyPeriod + type: INT + default: 2 +steps: + - name: TestStep + script: + actions: + onRun: + command: bash + args: ["-c", "echo 'Notify period works!'"] + cancelation: + mode: NOTIFY_THEN_TERMINATE + notifyPeriodInSeconds: "{{Param.NotifyPeriod}}" diff --git a/test/openjd/cli/templates/feature_bundle_1_powershell.yaml b/test/openjd/cli/templates/feature_bundle_1_powershell.yaml new file mode 100644 index 0000000..888819c --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_powershell.yaml @@ -0,0 +1,9 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 PowerShell Syntax Sugar +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: PowerShellStep + powershell: + script: | + Write-Host "Hello from PowerShell!" diff --git a/test/openjd/cli/templates/feature_bundle_1_python.yaml b/test/openjd/cli/templates/feature_bundle_1_python.yaml new file mode 100644 index 0000000..400ff26 --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_python.yaml @@ -0,0 +1,9 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 Python Syntax Sugar +extensions: + - FEATURE_BUNDLE_1 +steps: + - name: PythonStep + python: + script: | + print("Hello from Python!") diff --git a/test/openjd/cli/templates/feature_bundle_1_timeout.yaml b/test/openjd/cli/templates/feature_bundle_1_timeout.yaml new file mode 100644 index 0000000..3e21a31 --- /dev/null +++ b/test/openjd/cli/templates/feature_bundle_1_timeout.yaml @@ -0,0 +1,16 @@ +specificationVersion: jobtemplate-2023-09 +name: FB1 Format String Timeout +extensions: + - FEATURE_BUNDLE_1 +parameterDefinitions: + - name: Timeout + type: INT + default: 5 +steps: + - name: TimeoutStep + script: + actions: + onRun: + command: bash + args: ["-c", "echo Running with timeout {{Param.Timeout}}s; sleep 1"] + timeout: "{{Param.Timeout}}" diff --git a/test/openjd/cli/test_feature_bundle_1.py b/test/openjd/cli/test_feature_bundle_1.py new file mode 100644 index 0000000..09079a6 --- /dev/null +++ b/test/openjd/cli/test_feature_bundle_1.py @@ -0,0 +1,95 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +"""Tests for FEATURE_BUNDLE_1 extension support in the CLI.""" + +import os +from pathlib import Path + +import pytest + +from . import run_openjd_cli_main + +TEMPLATES_DIR = Path(__file__).parent / "templates" + + +class TestFeatureBundle1: + """Tests for FEATURE_BUNDLE_1 extension features.""" + + def test_python_syntax_sugar(self, capsys) -> None: + """Test that Python syntax sugar works.""" + template = TEMPLATES_DIR / "feature_bundle_1_python.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Hello from Python!" in outerr.out + + def test_bash_syntax_sugar(self, capsys) -> None: + """Test that Bash syntax sugar works.""" + template = TEMPLATES_DIR / "feature_bundle_1_bash.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Hello from Bash!" in outerr.out + + @pytest.mark.skipif(os.name != "nt", reason="PowerShell only available on Windows") + def test_powershell_syntax_sugar(self, capsys) -> None: + """Test that PowerShell syntax sugar works.""" + template = TEMPLATES_DIR / "feature_bundle_1_powershell.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Hello from PowerShell!" in outerr.out + + @pytest.mark.skipif(os.name != "nt", reason="cmd only available on Windows") + def test_cmd_syntax_sugar(self, capsys) -> None: + """Test that cmd syntax sugar works.""" + template = TEMPLATES_DIR / "feature_bundle_1_cmd.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Hello from Cmd!" in outerr.out + + def test_format_string_timeout(self, capsys) -> None: + """Test that format string timeout is resolved.""" + template = TEMPLATES_DIR / "feature_bundle_1_timeout.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Running with timeout 5s" in outerr.out + + def test_format_string_amount_minmax(self, capsys) -> None: + """Test that format string min/max in AmountRequirement is resolved.""" + template = TEMPLATES_DIR / "feature_bundle_1_amount_minmax.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Amount min/max works!" in outerr.out + + def test_format_string_notify_period(self, capsys) -> None: + """Test that format string notifyPeriodInSeconds is resolved.""" + template = TEMPLATES_DIR / "feature_bundle_1_notify_period.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Notify period works!" in outerr.out + + def test_extended_step_name(self, capsys) -> None: + """Test that extended step names (>64 chars) work with extension.""" + template = TEMPLATES_DIR / "feature_bundle_1_long_name.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + assert "Long step name works!" in outerr.out + + def test_end_of_line_lf(self, capsys) -> None: + """Test that endOfLine: LF produces LF-only line endings.""" + template = TEMPLATES_DIR / "feature_bundle_1_eol_lf.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + # xxd output: 0a is LF, no 0d (CR) present + assert "310a 6c69 6e65 320a 6c69" in outerr.out # line1line2line + + def test_end_of_line_crlf(self, capsys) -> None: + """Test that endOfLine: CRLF produces CRLF line endings.""" + template = TEMPLATES_DIR / "feature_bundle_1_eol_crlf.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + # xxd output: 0d0a is CRLF + assert "310d 0a6c 696e 6532 0d0a" in outerr.out # line1line2 + + def test_end_of_line_auto(self, capsys) -> None: + """Test that endOfLine: AUTO produces platform-native line endings.""" + template = TEMPLATES_DIR / "feature_bundle_1_eol_auto.yaml" + outerr = run_openjd_cli_main(capsys, args=["run", str(template)], expected_exit_code=0) + if os.name == "nt": + assert "310d 0a6c 696e 6532 0d0a" in outerr.out # CRLF on Windows + else: + assert "310a 6c69 6e65 320a 6c69" in outerr.out # LF on POSIX + + def test_check_validates_extension(self, capsys) -> None: + """Test that check command validates templates with extension.""" + template = TEMPLATES_DIR / "feature_bundle_1_python.yaml" + outerr = run_openjd_cli_main(capsys, args=["check", str(template)], expected_exit_code=0) + assert "passes validation checks" in outerr.out