ray-mcp-server 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. ray_mcp_server-0.2.0/.claude/settings.local.json +10 -0
  2. ray_mcp_server-0.2.0/.github/ISSUE_TEMPLATE/release.md +68 -0
  3. ray_mcp_server-0.2.0/.github/workflows/ci.yml +66 -0
  4. ray_mcp_server-0.2.0/.github/workflows/publish.yml +141 -0
  5. ray_mcp_server-0.2.0/.gitignore +206 -0
  6. ray_mcp_server-0.2.0/.python-version +1 -0
  7. ray_mcp_server-0.2.0/CLAUDE.md +138 -0
  8. ray_mcp_server-0.2.0/LICENSE +200 -0
  9. ray_mcp_server-0.2.0/Makefile +125 -0
  10. ray_mcp_server-0.2.0/NOTICE +40 -0
  11. ray_mcp_server-0.2.0/PKG-INFO +312 -0
  12. ray_mcp_server-0.2.0/README.md +277 -0
  13. ray_mcp_server-0.2.0/docs/CONFIGURATION.md +127 -0
  14. ray_mcp_server-0.2.0/docs/DEVELOPMENT.md +219 -0
  15. ray_mcp_server-0.2.0/docs/EXAMPLES.md +262 -0
  16. ray_mcp_server-0.2.0/docs/KUBERNETES_SETUP.md +126 -0
  17. ray_mcp_server-0.2.0/docs/PUBLISHING.md +143 -0
  18. ray_mcp_server-0.2.0/docs/TOOLS.md +176 -0
  19. ray_mcp_server-0.2.0/docs/TROUBLESHOOTING.md +222 -0
  20. ray_mcp_server-0.2.0/docs/config/README.md +173 -0
  21. ray_mcp_server-0.2.0/docs/config/claude_desktop_config.json +11 -0
  22. ray_mcp_server-0.2.0/docs/config/mcp_server_config.json +79 -0
  23. ray_mcp_server-0.2.0/examples/actor_example.py +153 -0
  24. ray_mcp_server-0.2.0/examples/data_pipeline.py +235 -0
  25. ray_mcp_server-0.2.0/examples/distributed_training.py +326 -0
  26. ray_mcp_server-0.2.0/examples/multi_node_cluster.py +95 -0
  27. ray_mcp_server-0.2.0/examples/simple_job.py +78 -0
  28. ray_mcp_server-0.2.0/examples/workflow_orchestration.py +482 -0
  29. ray_mcp_server-0.2.0/pyproject.toml +132 -0
  30. ray_mcp_server-0.2.0/pytest.ini +29 -0
  31. ray_mcp_server-0.2.0/ray_mcp/__init__.py +5 -0
  32. ray_mcp_server-0.2.0/ray_mcp/main.py +428 -0
  33. ray_mcp_server-0.2.0/ray_mcp/ray_manager.py +1079 -0
  34. ray_mcp_server-0.2.0/ray_mcp/tools.py +191 -0
  35. ray_mcp_server-0.2.0/ray_mcp/types.py +281 -0
  36. ray_mcp_server-0.2.0/ray_mcp/worker_manager.py +241 -0
  37. ray_mcp_server-0.2.0/scripts/smart-test.sh +62 -0
  38. ray_mcp_server-0.2.0/scripts/test-e2e.sh +15 -0
  39. ray_mcp_server-0.2.0/scripts/test-fast.sh +14 -0
  40. ray_mcp_server-0.2.0/scripts/test-full.sh +14 -0
  41. ray_mcp_server-0.2.0/scripts/test-smoke.sh +12 -0
  42. ray_mcp_server-0.2.0/tests/README.md +275 -0
  43. ray_mcp_server-0.2.0/tests/test_e2e_integration.py +1386 -0
  44. ray_mcp_server-0.2.0/tests/test_integration.py +354 -0
  45. ray_mcp_server-0.2.0/tests/test_main.py +571 -0
  46. ray_mcp_server-0.2.0/tests/test_mcp_tools.py +475 -0
  47. ray_mcp_server-0.2.0/tests/test_multi_node_cluster.py +254 -0
  48. ray_mcp_server-0.2.0/tests/test_ray_manager.py +717 -0
  49. ray_mcp_server-0.2.0/tests/test_ray_manager_methods.py +749 -0
  50. ray_mcp_server-0.2.0/tests/test_tools.py +470 -0
  51. ray_mcp_server-0.2.0/tests/test_worker_manager.py +887 -0
  52. ray_mcp_server-0.2.0/uv.lock +1583 -0
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(export TWINE_USERNAME=__token__)",
5
+ "Bash(export:*)",
6
+ "Bash(uv tool install:*)"
7
+ ],
8
+ "deny": []
9
+ }
10
+ }
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: Release Checklist
3
+ about: Checklist for creating a new release
4
+ title: 'Release v[VERSION]'
5
+ labels: release
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Release Checklist for v[VERSION]
11
+
12
+ ### Pre-release
13
+ - [ ] Update version in `pyproject.toml`
14
+ - [ ] Update version references in documentation
15
+ - [ ] Run full test suite: `uv run pytest`
16
+ - [ ] Run linting: `make lint`
17
+ - [ ] Test local build: `uv build`
18
+ - [ ] Test local installation: `uvx --from ./dist/ray_mcp_server-[VERSION]-py3-none-any.whl ray-mcp-server`
19
+
20
+ ### Release Process
21
+ - [ ] Commit all changes
22
+ - [ ] Create and push tag: `git tag v[VERSION] && git push origin v[VERSION]`
23
+ - [ ] Verify GitHub Actions workflow completes successfully
24
+ - [ ] Verify package appears on PyPI: https://pypi.org/p/ray-mcp-server/
25
+ - [ ] Create GitHub release with release notes
26
+
27
+ ### Post-release
28
+ - [ ] Test installation from PyPI: `uvx ray-mcp-server`
29
+ - [ ] Update Claude Desktop config example in README
30
+ - [ ] Update `docs/config/claude_desktop_config.json` if needed
31
+ - [ ] Announce release (if applicable)
32
+
33
+ ### Release Notes Template
34
+
35
+ ```markdown
36
+ ## What's New in v[VERSION]
37
+
38
+ ### ✨ Features
39
+ -
40
+
41
+ ### 🐛 Bug Fixes
42
+ -
43
+
44
+ ### 📚 Documentation
45
+ -
46
+
47
+ ### 🔧 Technical Changes
48
+ -
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ uvx ray-mcp-server
54
+ ```
55
+
56
+ ## Claude Desktop Configuration
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "ray-mcp": {
62
+ "command": "uvx",
63
+ "args": ["ray-mcp-server"]
64
+ }
65
+ }
66
+ }
67
+ ```
68
+ ```
@@ -0,0 +1,66 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main ]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test-fast:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.12"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v4
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v1
27
+ with:
28
+ version: "latest"
29
+
30
+ - name: Install dependencies
31
+ run: uv sync --all-extras --dev
32
+
33
+ - name: Run fast test suite
34
+ run: python -m pytest tests/ -m "fast" --tb=short -v --maxfail=3 --cov=ray_mcp --cov-report=xml --cov-report=term-missing
35
+
36
+ - name: Upload coverage to Codecov
37
+ uses: codecov/codecov-action@v3
38
+ with:
39
+ file: ./coverage.xml
40
+ fail_ci_if_error: false
41
+ verbose: true
42
+
43
+ lint:
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+
48
+ - name: Set up Python 3.12
49
+ uses: actions/setup-python@v4
50
+ with:
51
+ python-version: "3.12"
52
+
53
+ - name: Install uv
54
+ uses: astral-sh/setup-uv@v1
55
+ with:
56
+ version: "latest"
57
+
58
+ - name: Install dependencies
59
+ run: uv sync --dev
60
+
61
+ - name: Run linting
62
+ run: |
63
+ uv run black --check ray_mcp/ examples/ tests/
64
+ uv run isort --check-only ray_mcp/ examples/ tests/
65
+ uv run pyright ray_mcp/ examples/ tests/
66
+
@@ -0,0 +1,141 @@
1
+ name: Build and Publish
2
+
3
+ on:
4
+ # Manual trigger
5
+ workflow_dispatch:
6
+ inputs:
7
+ target:
8
+ description: 'Target repository'
9
+ required: true
10
+ default: 'testpypi'
11
+ type: choice
12
+ options:
13
+ - testpypi
14
+ - pypi
15
+
16
+ # Automatic trigger on GitHub releases
17
+ release:
18
+ types: [published]
19
+
20
+ # Trigger on tags for releases
21
+ push:
22
+ tags:
23
+ - 'v*'
24
+
25
+ jobs:
26
+ build:
27
+ name: Build distribution
28
+ runs-on: ubuntu-latest
29
+
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+
33
+ - name: Set up Python
34
+ uses: actions/setup-python@v4
35
+ with:
36
+ python-version: "3.12"
37
+
38
+ - name: Install uv
39
+ uses: astral-sh/setup-uv@v1
40
+ with:
41
+ version: "latest"
42
+
43
+ - name: Install dependencies
44
+ run: uv sync --dev
45
+
46
+ - name: Build package
47
+ run: uv build
48
+
49
+ - name: Store the distribution packages
50
+ uses: actions/upload-artifact@v3
51
+ with:
52
+ name: python-package-distributions
53
+ path: dist/
54
+
55
+ publish-to-pypi:
56
+ name: Publish to PyPI
57
+ if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'pypi')
58
+ needs:
59
+ - build
60
+ runs-on: ubuntu-latest
61
+ environment:
62
+ name: pypi
63
+ url: https://pypi.org/p/ray-mcp-server
64
+ permissions:
65
+ id-token: write # IMPORTANT: mandatory for trusted publishing
66
+
67
+ steps:
68
+ - name: Download all the dists
69
+ uses: actions/download-artifact@v3
70
+ with:
71
+ name: python-package-distributions
72
+ path: dist/
73
+
74
+ - name: Publish to PyPI
75
+ uses: pypa/gh-action-pypi-publish@release/v1
76
+ with:
77
+ repository-url: https://upload.pypi.org/legacy/
78
+
79
+ publish-to-testpypi:
80
+ name: Publish to TestPyPI
81
+ if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi'
82
+ needs:
83
+ - build
84
+ runs-on: ubuntu-latest
85
+ environment:
86
+ name: testpypi
87
+ url: https://test.pypi.org/p/ray-mcp-server
88
+
89
+ steps:
90
+ - name: Download all the dists
91
+ uses: actions/download-artifact@v3
92
+ with:
93
+ name: python-package-distributions
94
+ path: dist/
95
+
96
+ - name: Publish to TestPyPI
97
+ uses: pypa/gh-action-pypi-publish@release/v1
98
+ with:
99
+ repository-url: https://test.pypi.org/legacy/
100
+ password: ${{ secrets.TEST_PYPI_API_TOKEN }}
101
+
102
+ github-release:
103
+ name: Create GitHub Release
104
+ if: startsWith(github.ref, 'refs/tags/')
105
+ needs:
106
+ - publish-to-pypi
107
+ runs-on: ubuntu-latest
108
+ permissions:
109
+ contents: write
110
+ id-token: write
111
+
112
+ steps:
113
+ - name: Download all the dists
114
+ uses: actions/download-artifact@v3
115
+ with:
116
+ name: python-package-distributions
117
+ path: dist/
118
+
119
+ - name: Sign the dists with Sigstore
120
+ uses: sigstore/gh-action-sigstore-python@v2.1.1
121
+ with:
122
+ inputs: >-
123
+ ./dist/*.tar.gz
124
+ ./dist/*.whl
125
+
126
+ - name: Create GitHub Release
127
+ env:
128
+ GITHUB_TOKEN: ${{ github.token }}
129
+ run: >-
130
+ gh release create
131
+ '${{ github.ref_name }}'
132
+ --repo '${{ github.repository }}'
133
+ --notes ""
134
+
135
+ - name: Upload artifact signatures to GitHub Release
136
+ env:
137
+ GITHUB_TOKEN: ${{ github.token }}
138
+ run: >-
139
+ gh release upload
140
+ '${{ github.ref_name }}' dist/**
141
+ --repo '${{ github.repository }}'
@@ -0,0 +1,206 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .coverage_data/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # poetry
99
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103
+ #poetry.lock
104
+
105
+ # pdm
106
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
107
+ #pdm.lock
108
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
109
+ # in version control.
110
+ # https://pdm.fming.dev/#use-with-ide
111
+ .pdm.toml
112
+
113
+ # uv
114
+ # uv.lock should be committed to version control for reproducible builds
115
+ .uv/
116
+
117
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
118
+ __pypackages__/
119
+
120
+ # Celery stuff
121
+ celerybeat-schedule
122
+ celerybeat.pid
123
+
124
+ # SageMath parsed files
125
+ *.sage.py
126
+
127
+ # Virtual environments
128
+ .env
129
+ .venv
130
+ env/
131
+ venv/
132
+ ENV/
133
+ env.bak/
134
+ venv.bak/
135
+
136
+ # Spyder project settings
137
+ .spyderproject
138
+ .spyproject
139
+
140
+ # Rope project settings
141
+ .ropeproject
142
+
143
+ # mkdocs documentation
144
+ /site
145
+
146
+ # mypy
147
+ .mypy_cache/
148
+ .dmypy.json
149
+ dmypy.json
150
+
151
+ # Pyre type checker
152
+ .pyre/
153
+
154
+ # pytype static type analyzer
155
+ .pytype/
156
+
157
+ # Cython debug symbols
158
+ cython_debug/
159
+
160
+ # IDEs and editors
161
+ .vscode/
162
+ .idea/
163
+ *.swp
164
+ *.swo
165
+ *~
166
+ *.sublime-project
167
+ *.sublime-workspace
168
+ .vscode/*
169
+ !.vscode/settings.json
170
+ !.vscode/tasks.json
171
+ !.vscode/launch.json
172
+ !.vscode/extensions.json
173
+ *.code-workspace
174
+
175
+ # OS generated files
176
+ .DS_Store
177
+ .DS_Store?
178
+ ._*
179
+ .Spotlight-V100
180
+ .Trashes
181
+ ehthumbs.db
182
+ Thumbs.db
183
+ Desktop.ini
184
+
185
+ # Ray specific
186
+ ray_logs/
187
+ ray_mcp_logs/
188
+ /tmp/ray/
189
+
190
+ # Project specific
191
+ cleanup_repo.sh
192
+ .env.local
193
+ .env.development
194
+ .env.test
195
+ .env.production
196
+
197
+ # Temporary files
198
+ *.tmp
199
+ *.temp
200
+ .tmp/
201
+ temp/
202
+
203
+ # Backup files
204
+ *.bak
205
+ *.backup
206
+ *~
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,138 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ This is a Ray MCP (Model Context Protocol) server that provides comprehensive Ray cluster management, job execution, and distributed computing capabilities. The server acts as an MCP bridge to Ray, enabling LLMs to manage Ray clusters and jobs through structured tool calls.
8
+
9
+ ## Architecture
10
+
11
+ - **`ray_mcp/main.py`**: Main MCP server entry point with tool definitions and request handlers
12
+ - **`ray_mcp/ray_manager.py`**: Core Ray cluster management (RayManager class)
13
+ - **`ray_mcp/worker_manager.py`**: Multi-node worker management and orchestration
14
+ - **`ray_mcp/tools.py`**: MCP tool implementations and validation
15
+ - **`ray_mcp/types.py`**: Type definitions and Pydantic models for all data structures
16
+ - **`examples/`**: Working examples demonstrating Ray usage patterns
17
+ - **`tests/`**: Comprehensive test suite with multiple test categories
18
+
19
+ ## Key Development Commands
20
+
21
+ ### Testing
22
+ ```bash
23
+ # Fast development tests (excludes e2e)
24
+ make test
25
+
26
+ # Full test suite including e2e tests
27
+ make test-full
28
+
29
+ # Smart test runner (detects changes)
30
+ make test-smart
31
+
32
+ # Specific test categories
33
+ make test-smoke # Basic functionality verification
34
+ make test-e2e # End-to-end integration tests
35
+ ```
36
+
37
+ ### Code Quality
38
+ ```bash
39
+ # Run all linting checks
40
+ make lint
41
+
42
+ # Auto-format code
43
+ make format
44
+
45
+ # Individual checks
46
+ uv run pyright ray_mcp/ # Type checking
47
+ uv run black ray_mcp/ # Code formatting
48
+ uv run isort ray_mcp/ # Import sorting
49
+ ```
50
+
51
+ ### Development Setup
52
+ ```bash
53
+ # Full development setup
54
+ make dev-install
55
+
56
+ # Install dependencies only
57
+ uv sync
58
+
59
+ # Run the MCP server
60
+ uv run ray-mcp
61
+ ```
62
+
63
+ ### Testing Infrastructure
64
+
65
+ The project uses pytest with custom markers:
66
+ - `fast`: Quick unit/integration tests for development
67
+ - `e2e`: End-to-end tests requiring full Ray cluster setup
68
+ - `smoke`: Minimal verification tests
69
+ - `slow`: Performance-intensive tests
70
+
71
+ Test files follow patterns: `test_*.py` in the `tests/` directory.
72
+
73
+ ## Multi-Node Architecture
74
+
75
+ The server supports both single-node and multi-node Ray clusters:
76
+ - **Default**: Creates head node + 2 worker nodes automatically
77
+ - **Custom**: Supports configurable worker node specifications
78
+ - **WorkerManager**: Handles worker lifecycle and resource management
79
+
80
+ ## Key Implementation Details
81
+
82
+ - All Ray operations are wrapped with proper error handling for Ray availability
83
+ - The server uses async/await patterns for MCP protocol compliance
84
+ - Type safety is enforced with Pydantic models and pyright type checking
85
+ - Multi-node clusters use localhost networking with configurable ports
86
+ - Job submission supports both simple scripts and complex workflow orchestration
87
+
88
+ ## Claude Desktop Setup
89
+
90
+ ### Local Development
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "ray-mcp": {
95
+ "command": "/opt/homebrew/bin/uv",
96
+ "args": ["run", "--directory", "/path/to/ray-mcp", "ray-mcp"]
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Connecting to Remote Ray Cluster
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "ray-mcp": {
107
+ "command": "/opt/homebrew/bin/uv",
108
+ "args": ["run", "--directory", "/path/to/ray-mcp", "ray-mcp"],
109
+ "env": {
110
+ "RAY_ADDRESS": "ray://127.0.0.1:10001"
111
+ }
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### Kubernetes Ray Cluster
118
+ For Kubernetes-deployed Ray clusters, port-forward the Ray client port:
119
+ ```bash
120
+ kubectl port-forward -n ray-cluster ray-cluster-kuberay-head-<pod-id> 10001:10001
121
+ ```
122
+
123
+ Then use `RAY_ADDRESS=ray://127.0.0.1:10001` in Claude Desktop config.
124
+
125
+ ### Version Compatibility
126
+ Ensure local Ray/Python versions match your cluster:
127
+ - Check cluster versions: `kubectl logs -n ray-cluster ray-cluster-kuberay-head-<pod-id>`
128
+ - Update `pyproject.toml` to match cluster Ray version
129
+ - Use `uv python pin <version>` to match Python version
130
+ - Run `uv sync` to update dependencies
131
+
132
+ ## Development Notes
133
+
134
+ - This project uses `uv` exclusively (not pip or python directly)
135
+ - All commands should use `uv run` prefix
136
+ - The codebase follows black formatting and isort import organization
137
+ - Coverage threshold is set to 80% minimum
138
+ - Test execution time is optimized with parallel test runners and smart test selection