gridfleet-agent 0.2.0__tar.gz → 0.2.2__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.
- gridfleet_agent-0.2.2/CHANGELOG.md +50 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/PKG-INFO +1 -1
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/installer/install.py +13 -2
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/installer/plan.py +32 -6
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/pyproject.toml +1 -1
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/installer/test_install.py +22 -2
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/installer/test_plan.py +37 -0
- gridfleet_agent-0.2.2/tests/test_install_script.py +141 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/uv.lock +1 -1
- gridfleet_agent-0.2.0/CHANGELOG.md +0 -28
- gridfleet_agent-0.2.0/tests/test_install_script.py +0 -58
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/.gitignore +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/README.md +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/__init__.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/appium_process.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/capabilities.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/cli.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/config.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/driver_doctor.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/host_telemetry.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/installer/__init__.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/installer/status.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/installer/uninstall.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/installer/update.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/main.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/observability.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/__init__.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/adapter_dispatch.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/adapter_loader.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/adapter_registry.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/adapter_types.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/adapter_utils.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/discovery.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/dispatch.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/host_identity.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/manifest.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/runtime.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/runtime_policy.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/runtime_registry.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/sidecar_supervisor.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/state.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/tarball_fetch.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/pack/version_catalog.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/plugin_manager.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/py.typed +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/registration.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/terminal_pty.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/terminal_ws.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/tool_paths.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/tool_utils.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/tools_manager.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/agent_app/version_guidance.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/__init__.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/installer/test_cli_install.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/installer/test_status.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/installer/test_uninstall.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/installer/test_update.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/__init__.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/conftest.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_dispatch.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_loader.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_loader_cancel_path_leak.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_loader_concurrent_load.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_tarball_auth.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_utils.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_wiring.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_appium_process_integration.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_desired_manifest_features.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_feature_action_routes.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_host_identity.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_manifest_parser.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_pack_discovery_endpoint.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_pack_state.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_pack_state_client_auth.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_pack_state_sidecar_reconcile.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_runtime_github.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_runtime_isolated_failures.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_runtime_manager.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_runtime_plugins.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_runtime_policy.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_sidecar_supervisor.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_sidecar_supervisor_poll_stop_race.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_state_loop.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_state_loop_wired.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_tarball_fetch.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_tarball_fetch_concurrent_dedup.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_version_catalog.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_agent_api.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_agent_api_more.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_port_alloc_race.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_restart_stop_race.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_stop_start_lock_race.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_watch_stop_race.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_capabilities.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_capabilities_more.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_cli.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_config.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_config_runtime_root.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_driver_doctor.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_host_telemetry.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_no_driver_imports.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_observability.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_package_metadata.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_plugin_manager.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_plugin_manager_more.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_registration.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_terminal_pty.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_terminal_ws.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_tools_and_utilities_more.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_tools_manager.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_tools_manager_extra.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_version_guidance.py +0 -0
- {gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/uninstall.sh +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog — GridFleet Agent
|
|
2
|
+
|
|
3
|
+
All notable changes to the GridFleet host agent (`gridfleet-agent` on PyPI) are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.2.2](https://github.com/quidow/gridfleet/compare/gridfleet-agent-v0.2.1...gridfleet-agent-v0.2.2) (2026-05-03)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **agent:** prefer nvm node during install ([b0e672b](https://github.com/quidow/gridfleet/commit/b0e672b22e761593c657ae6d54b665f0112a61df))
|
|
11
|
+
* **agent:** support sh installer and auth hint ([81cc1fd](https://github.com/quidow/gridfleet/commit/81cc1fd0fb2d344baa135b31665f216d7d607c75))
|
|
12
|
+
|
|
13
|
+
## [0.2.1](https://github.com/quidow/gridfleet/compare/gridfleet-agent-v0.2.0...gridfleet-agent-v0.2.1) (2026-05-02)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **agent:** close port-allocator and adapter-loader race windows ([#23](https://github.com/quidow/gridfleet/issues/23)) ([4bea799](https://github.com/quidow/gridfleet/commit/4bea799dd6f7931223ec2d2828de5c1e83bf8b8c))
|
|
19
|
+
* **agent:** dedup and isolate tarball_fetch targets ([#27](https://github.com/quidow/gridfleet/issues/27)) ([f83ac99](https://github.com/quidow/gridfleet/commit/f83ac991b8b7f9d1916b64fc465187f1995274c7))
|
|
20
|
+
* **agent:** hold _start_lock across AppiumProcessManager.stop() body ([#24](https://github.com/quidow/gridfleet/issues/24)) ([a42f1da](https://github.com/quidow/gridfleet/commit/a42f1da759e52add383e9eea0852a85d5633c4e8))
|
|
21
|
+
* **agent:** idempotent bootstrap installer with sudo and launchd handling ([#51](https://github.com/quidow/gridfleet/issues/51)) ([db0f059](https://github.com/quidow/gridfleet/commit/db0f059d5288979bbca314fbcf2e92e09e888be8))
|
|
22
|
+
* **agent:** reset to 0.2.0, drop --locked from ci ([c6ee2ea](https://github.com/quidow/gridfleet/commit/c6ee2eab4ba7d4b761136cdea1a929d6e22bca3f))
|
|
23
|
+
* **agent:** use importlib.metadata for version, fix publish lock files ([b96a112](https://github.com/quidow/gridfleet/commit/b96a112db50ef8e7c8d5bd1524104d7f27cb5afd))
|
|
24
|
+
* authenticate agent driver pack tarball fetches ([898859e](https://github.com/quidow/gridfleet/commit/898859eae0ced10a6109058ac6aeab4b6c851934))
|
|
25
|
+
* **ci:** update agent lock file, add auto-lockfile workflow, fix local commitlint hook ([920b71e](https://github.com/quidow/gridfleet/commit/920b71eeaa942b33c711a3dcb75115b37525947c))
|
|
26
|
+
|
|
27
|
+
## 0.2.0
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
- Rewrite bootstrap installer to use `uv tool install` instead of manual venv creation. Users no longer need Python 3.12+ pre-installed — `uv` handles it.
|
|
32
|
+
- Replace `validate_dedicated_venv` with `resolve_bin_path` — the agent no longer requires running from `/opt/gridfleet-agent/venv/bin/`. Supports `uv tool install` paths natively.
|
|
33
|
+
- Add `bin_path` to `InstallConfig` for configurable binary resolution in service unit templates (systemd/launchd).
|
|
34
|
+
- Replace `pip install --upgrade` with `uv tool upgrade gridfleet-agent` in the update flow.
|
|
35
|
+
- Add upgrade awareness: the agent caches version guidance from the manager's registration response and surfaces it on `/agent/health`, `HealthCheckResult.details`, and `gridfleet-agent status` CLI output.
|
|
36
|
+
- Use `importlib.metadata` for runtime version resolution — eliminates version sync issues between `pyproject.toml` and source.
|
|
37
|
+
|
|
38
|
+
### Fixes
|
|
39
|
+
|
|
40
|
+
- Update CLI tests for removed venv validation guard.
|
|
41
|
+
- Close port-allocator and adapter-loader race windows.
|
|
42
|
+
- Deduplicate and isolate tarball fetch targets.
|
|
43
|
+
- Hold `_start_lock` across `AppiumProcessManager.stop()` body.
|
|
44
|
+
- Authenticate agent driver-pack tarball fetches.
|
|
45
|
+
|
|
46
|
+
## 0.1.0 — Initial Public Preview
|
|
47
|
+
|
|
48
|
+
- Initial public preview of the GridFleet host agent.
|
|
49
|
+
- FastAPI agent that runs on each device host, spawning Appium processes and Selenium Grid relay nodes.
|
|
50
|
+
- Driver-pack runtime with manifest-driven adapter loading and isolated APPIUM_HOME.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import getpass
|
|
4
5
|
import hashlib
|
|
5
6
|
import os
|
|
@@ -213,7 +214,10 @@ def _start_service(
|
|
|
213
214
|
return
|
|
214
215
|
if os_name == "Darwin":
|
|
215
216
|
resolved_uid = _resolve_uid(uid)
|
|
216
|
-
|
|
217
|
+
domain_target = f"gui/{resolved_uid}"
|
|
218
|
+
with contextlib.suppress(RuntimeError):
|
|
219
|
+
run_command(["launchctl", "bootout", f"{domain_target}/com.gridfleet.agent"])
|
|
220
|
+
run_command(["launchctl", "bootstrap", domain_target, str(service_file)])
|
|
217
221
|
return
|
|
218
222
|
raise RuntimeError(f"Unsupported OS: {os_name}")
|
|
219
223
|
|
|
@@ -287,7 +291,14 @@ def poll_manager_registration(
|
|
|
287
291
|
)
|
|
288
292
|
last_error = f"{resolved_hostname} was not listed"
|
|
289
293
|
else:
|
|
290
|
-
|
|
294
|
+
if status_code == 401:
|
|
295
|
+
last_error = (
|
|
296
|
+
"manager requires machine auth; rerun install with "
|
|
297
|
+
"--manager-auth-username and --manager-auth-password matching "
|
|
298
|
+
"GRIDFLEET_MACHINE_AUTH_USERNAME and GRIDFLEET_MACHINE_AUTH_PASSWORD"
|
|
299
|
+
)
|
|
300
|
+
else:
|
|
301
|
+
last_error = f"unexpected status {status_code}"
|
|
291
302
|
except Exception as exc:
|
|
292
303
|
last_error = str(exc)
|
|
293
304
|
time.sleep(interval_sec)
|
|
@@ -125,13 +125,15 @@ def _find_java(env: Mapping[str, str], home: Path, os_name: str) -> tuple[str |
|
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
def _find_node_bin_dir(env: Mapping[str, str], home: Path) -> str | None:
|
|
128
|
-
|
|
129
|
-
if
|
|
130
|
-
|
|
128
|
+
nvm_bin = env.get("NVM_BIN", "")
|
|
129
|
+
if nvm_bin:
|
|
130
|
+
executable = _first_existing_executable([f"{nvm_bin}/node"])
|
|
131
|
+
if executable:
|
|
132
|
+
return str(Path(executable).parent)
|
|
131
133
|
|
|
132
134
|
nvm_root = home / ".nvm/versions/node"
|
|
133
135
|
if nvm_root.is_dir():
|
|
134
|
-
candidates = sorted(nvm_root.glob("v*/bin/node"), reverse=True)
|
|
136
|
+
candidates = sorted(nvm_root.glob("v*/bin/node"), key=_node_version_key, reverse=True)
|
|
135
137
|
executable = _first_existing_executable([str(candidate) for candidate in candidates])
|
|
136
138
|
if executable:
|
|
137
139
|
return str(Path(executable).parent)
|
|
@@ -145,9 +147,23 @@ def _find_node_bin_dir(env: Mapping[str, str], home: Path) -> str | None:
|
|
|
145
147
|
if executable:
|
|
146
148
|
return str(Path(executable).parent)
|
|
147
149
|
|
|
150
|
+
node = shutil.which("node")
|
|
151
|
+
if node:
|
|
152
|
+
return str(Path(node).parent)
|
|
153
|
+
|
|
148
154
|
return None
|
|
149
155
|
|
|
150
156
|
|
|
157
|
+
def _node_version_key(node_path: Path) -> tuple[int, ...]:
|
|
158
|
+
version = node_path.parent.parent.name.removeprefix("v")
|
|
159
|
+
parts: list[int] = []
|
|
160
|
+
for segment in version.split("."):
|
|
161
|
+
if not segment.isdecimal():
|
|
162
|
+
break
|
|
163
|
+
parts.append(int(segment))
|
|
164
|
+
return tuple(parts)
|
|
165
|
+
|
|
166
|
+
|
|
151
167
|
def _find_android_home(env: Mapping[str, str], home: Path) -> str | None:
|
|
152
168
|
for candidate in (
|
|
153
169
|
env.get("ANDROID_HOME", ""),
|
|
@@ -162,14 +178,24 @@ def _find_android_home(env: Mapping[str, str], home: Path) -> str | None:
|
|
|
162
178
|
return None
|
|
163
179
|
|
|
164
180
|
|
|
181
|
+
def _operator_home(env: Mapping[str, str]) -> Path:
|
|
182
|
+
sudo_user = env.get("SUDO_USER")
|
|
183
|
+
if sudo_user:
|
|
184
|
+
try:
|
|
185
|
+
return Path(f"~{sudo_user}").expanduser()
|
|
186
|
+
except RuntimeError:
|
|
187
|
+
pass
|
|
188
|
+
return Path.home()
|
|
189
|
+
|
|
190
|
+
|
|
165
191
|
def discover_tools(
|
|
166
192
|
*,
|
|
167
193
|
env: Mapping[str, str] | None = None,
|
|
168
194
|
home: Path | None = None,
|
|
169
195
|
os_name: str | None = None,
|
|
170
196
|
) -> ToolDiscovery:
|
|
171
|
-
resolved_env = env
|
|
172
|
-
resolved_home = home or
|
|
197
|
+
resolved_env = os.environ if env is None else env
|
|
198
|
+
resolved_home = home or _operator_home(resolved_env)
|
|
173
199
|
resolved_os = os_name or platform.system()
|
|
174
200
|
warnings: list[str] = []
|
|
175
201
|
|
|
@@ -279,7 +279,8 @@ def test_install_with_start_skips_manager_registration_when_health_fails(tmp_pat
|
|
|
279
279
|
assert result.registration is None
|
|
280
280
|
|
|
281
281
|
|
|
282
|
-
def test_install_with_start_runs_launchctl_bootstrap_on_macos(tmp_path: Path) -> None:
|
|
282
|
+
def test_install_with_start_runs_launchctl_bootstrap_on_macos(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
283
|
+
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
283
284
|
config = _make_config(tmp_path)
|
|
284
285
|
executable = Path(config.venv_bin_dir) / "gridfleet-agent"
|
|
285
286
|
executable.parent.mkdir(parents=True)
|
|
@@ -299,7 +300,10 @@ def test_install_with_start_runs_launchctl_bootstrap_on_macos(tmp_path: Path) ->
|
|
|
299
300
|
|
|
300
301
|
assert result.started is True
|
|
301
302
|
assert result.health == HealthCheckResult(ok=False, message="health check timed out")
|
|
302
|
-
assert commands == [
|
|
303
|
+
assert commands == [
|
|
304
|
+
["launchctl", "bootout", "gui/0/com.gridfleet.agent"],
|
|
305
|
+
["launchctl", "bootstrap", "gui/0", str(result.service_file)],
|
|
306
|
+
]
|
|
303
307
|
|
|
304
308
|
|
|
305
309
|
def test_poll_manager_registration_returns_success_when_hostname_is_listed() -> None:
|
|
@@ -347,6 +351,22 @@ def test_poll_manager_registration_times_out_when_hostname_is_missing() -> None:
|
|
|
347
351
|
assert "agent-host was not listed" in result.message
|
|
348
352
|
|
|
349
353
|
|
|
354
|
+
def test_poll_manager_registration_explains_auth_required_on_401() -> None:
|
|
355
|
+
config = InstallConfig(manager_url="https://manager.example.com")
|
|
356
|
+
|
|
357
|
+
def fake_get(_url: str, timeout: float = 2.0, auth: tuple[str, str] | None = None) -> object:
|
|
358
|
+
class Response:
|
|
359
|
+
status_code = 401
|
|
360
|
+
|
|
361
|
+
return Response()
|
|
362
|
+
|
|
363
|
+
result = poll_manager_registration(config, hostname="agent-host", timeout_sec=0.01, interval_sec=0.01, get=fake_get)
|
|
364
|
+
|
|
365
|
+
assert result.ok is False
|
|
366
|
+
assert "--manager-auth-username" in result.message
|
|
367
|
+
assert "--manager-auth-password" in result.message
|
|
368
|
+
|
|
369
|
+
|
|
350
370
|
def test_install_with_start_raises_when_service_command_fails(tmp_path: Path) -> None:
|
|
351
371
|
config = _make_config(tmp_path)
|
|
352
372
|
executable = Path(config.venv_bin_dir) / "gridfleet-agent"
|
|
@@ -7,7 +7,9 @@ import pytest
|
|
|
7
7
|
from agent_app.installer.plan import (
|
|
8
8
|
InstallConfig,
|
|
9
9
|
ToolDiscovery,
|
|
10
|
+
_find_node_bin_dir,
|
|
10
11
|
build_service_path,
|
|
12
|
+
discover_tools,
|
|
11
13
|
format_dry_run,
|
|
12
14
|
load_installed_config,
|
|
13
15
|
render_config_env,
|
|
@@ -179,6 +181,41 @@ def test_build_service_path_prepends_discovered_tool_dirs() -> None:
|
|
|
179
181
|
assert build_service_path(discovery).startswith("/usr/lib/jvm/bin:/opt/node/bin:/opt/sdk/platform-tools:")
|
|
180
182
|
|
|
181
183
|
|
|
184
|
+
def test_find_node_bin_dir_prefers_home_nvm_over_system_node(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
185
|
+
nvm_node = tmp_path / ".nvm/versions/node/v24.12.0/bin/node"
|
|
186
|
+
nvm_node.parent.mkdir(parents=True)
|
|
187
|
+
nvm_node.write_text("")
|
|
188
|
+
nvm_node.chmod(0o755)
|
|
189
|
+
monkeypatch.setattr("agent_app.installer.plan.shutil.which", lambda _name: "/usr/bin/node")
|
|
190
|
+
|
|
191
|
+
assert _find_node_bin_dir({}, tmp_path) == str(nvm_node.parent)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_find_node_bin_dir_uses_highest_nvm_version(tmp_path: Path) -> None:
|
|
195
|
+
old_node = tmp_path / ".nvm/versions/node/v9.9.0/bin/node"
|
|
196
|
+
new_node = tmp_path / ".nvm/versions/node/v24.12.0/bin/node"
|
|
197
|
+
for node in (old_node, new_node):
|
|
198
|
+
node.parent.mkdir(parents=True)
|
|
199
|
+
node.write_text("")
|
|
200
|
+
node.chmod(0o755)
|
|
201
|
+
|
|
202
|
+
assert _find_node_bin_dir({}, tmp_path) == str(new_node.parent)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_discover_tools_uses_sudo_user_home_for_nvm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
206
|
+
sudo_home = tmp_path / "operator"
|
|
207
|
+
nvm_node = sudo_home / ".nvm/versions/node/v24.12.0/bin/node"
|
|
208
|
+
nvm_node.parent.mkdir(parents=True)
|
|
209
|
+
nvm_node.write_text("")
|
|
210
|
+
nvm_node.chmod(0o755)
|
|
211
|
+
monkeypatch.setattr("agent_app.installer.plan.Path.expanduser", lambda path: sudo_home)
|
|
212
|
+
monkeypatch.setattr("agent_app.installer.plan.shutil.which", lambda _name: "/usr/bin/node")
|
|
213
|
+
|
|
214
|
+
discovery = discover_tools(env={"SUDO_USER": "operator"}, os_name="Linux")
|
|
215
|
+
|
|
216
|
+
assert discovery.node_bin_dir == str(nvm_node.parent)
|
|
217
|
+
|
|
218
|
+
|
|
182
219
|
def test_config_resolved_bin_path_defaults_to_venv() -> None:
|
|
183
220
|
config = InstallConfig()
|
|
184
221
|
assert config.resolved_bin_path == "/opt/gridfleet-agent/venv/bin/gridfleet-agent"
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from stat import S_IXUSR
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _write_executable(path: Path, content: str) -> None:
|
|
10
|
+
path.write_text(content)
|
|
11
|
+
path.chmod(0o755)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_bootstrap_wrapper_uses_uv_tool_install() -> None:
|
|
15
|
+
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
16
|
+
assert script.startswith("#!/bin/sh")
|
|
17
|
+
assert "uv tool install" in script
|
|
18
|
+
assert "gridfleet-agent" in script
|
|
19
|
+
assert "--python 3.12" in script
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_bootstrap_wrapper_installs_uv_if_missing() -> None:
|
|
23
|
+
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
24
|
+
assert "astral.sh/uv/install.sh" in script
|
|
25
|
+
assert "command -v uv" in script
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_bootstrap_wrapper_calls_gridfleet_agent_install() -> None:
|
|
29
|
+
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
30
|
+
assert "gridfleet-agent install" in script
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_bootstrap_wrapper_is_executable() -> None:
|
|
34
|
+
script_path = Path(__file__).resolve().parents[2] / "scripts/install-agent.sh"
|
|
35
|
+
assert script_path.stat().st_mode & S_IXUSR
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_bootstrap_wrapper_runs_under_sh(tmp_path: Path) -> None:
|
|
39
|
+
bin_dir = tmp_path / "bin"
|
|
40
|
+
bin_dir.mkdir()
|
|
41
|
+
log = tmp_path / "commands.log"
|
|
42
|
+
env = os.environ | {"PATH": f"{bin_dir}:{os.environ['PATH']}", "COMMAND_LOG": str(log)}
|
|
43
|
+
script_path = Path(__file__).resolve().parents[2] / "scripts/install-agent.sh"
|
|
44
|
+
|
|
45
|
+
_write_executable(
|
|
46
|
+
bin_dir / "uv",
|
|
47
|
+
'#!/usr/bin/env bash\nprintf \'uv %s\\n\' "$*" >> "$COMMAND_LOG"\n',
|
|
48
|
+
)
|
|
49
|
+
_write_executable(
|
|
50
|
+
bin_dir / "gridfleet-agent",
|
|
51
|
+
'#!/usr/bin/env bash\nprintf \'gridfleet-agent %s\\n\' "$*" >> "$COMMAND_LOG"\n',
|
|
52
|
+
)
|
|
53
|
+
_write_executable(bin_dir / "uname", "#!/usr/bin/env bash\necho Linux\n")
|
|
54
|
+
_write_executable(bin_dir / "id", '#!/usr/bin/env bash\n[ "$1" = "-u" ] && echo 0\n')
|
|
55
|
+
|
|
56
|
+
result = subprocess.run(
|
|
57
|
+
["sh", str(script_path), "--dry-run", "--manager-url", "https://manager.example.com"],
|
|
58
|
+
check=False,
|
|
59
|
+
capture_output=True,
|
|
60
|
+
text=True,
|
|
61
|
+
env=env,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
assert result.returncode == 0, result.stderr
|
|
65
|
+
commands = log.read_text()
|
|
66
|
+
assert "uv tool install --upgrade --python 3.12 gridfleet-agent" in commands
|
|
67
|
+
assert "gridfleet-agent install --dry-run --manager-url https://manager.example.com" in commands
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_bootstrap_wrapper_supports_version_pinning() -> None:
|
|
71
|
+
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
72
|
+
assert "VERSION" in script
|
|
73
|
+
assert "gridfleet-agent==" in script
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_bootstrap_wrapper_defaults_to_start_mode() -> None:
|
|
77
|
+
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
78
|
+
assert "--start" in script
|
|
79
|
+
assert "--dry-run" in script
|
|
80
|
+
assert "--no-start" in script
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.parametrize("mode_args", [("--dry-run",), ("--no-start",), ("--start", "--dry-run")])
|
|
84
|
+
def test_bootstrap_wrapper_only_stops_service_for_start_mode(tmp_path: Path, mode_args: tuple[str, ...]) -> None:
|
|
85
|
+
bin_dir = tmp_path / "bin"
|
|
86
|
+
bin_dir.mkdir()
|
|
87
|
+
log = tmp_path / "commands.log"
|
|
88
|
+
env = os.environ | {"PATH": f"{bin_dir}:{os.environ['PATH']}", "COMMAND_LOG": str(log)}
|
|
89
|
+
script_path = Path(__file__).resolve().parents[2] / "scripts/install-agent.sh"
|
|
90
|
+
|
|
91
|
+
_write_executable(
|
|
92
|
+
bin_dir / "uv",
|
|
93
|
+
'#!/usr/bin/env bash\nprintf \'uv %s\\n\' "$*" >> "$COMMAND_LOG"\n',
|
|
94
|
+
)
|
|
95
|
+
_write_executable(
|
|
96
|
+
bin_dir / "gridfleet-agent",
|
|
97
|
+
'#!/usr/bin/env bash\nprintf \'gridfleet-agent %s\\n\' "$*" >> "$COMMAND_LOG"\n',
|
|
98
|
+
)
|
|
99
|
+
_write_executable(
|
|
100
|
+
bin_dir / "systemctl",
|
|
101
|
+
'#!/usr/bin/env bash\nprintf \'systemctl %s\\n\' "$*" >> "$COMMAND_LOG"\n',
|
|
102
|
+
)
|
|
103
|
+
_write_executable(bin_dir / "uname", "#!/usr/bin/env bash\necho Linux\n")
|
|
104
|
+
_write_executable(bin_dir / "id", '#!/usr/bin/env bash\n[ "$1" = "-u" ] && echo 1000\n')
|
|
105
|
+
_write_executable(
|
|
106
|
+
bin_dir / "sudo",
|
|
107
|
+
'#!/usr/bin/env bash\nprintf \'sudo %s\\n\' "$*" >> "$COMMAND_LOG"\n"$@"\n',
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
result = subprocess.run(
|
|
111
|
+
[str(script_path), *mode_args, "--manager-url", "https://manager.example.com"],
|
|
112
|
+
check=False,
|
|
113
|
+
capture_output=True,
|
|
114
|
+
text=True,
|
|
115
|
+
env=env,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
assert result.returncode == 0, result.stderr
|
|
119
|
+
commands = log.read_text()
|
|
120
|
+
assert "systemctl stop gridfleet-agent" not in commands
|
|
121
|
+
assert f"gridfleet-agent install {' '.join(mode_args)} --manager-url https://manager.example.com" in commands
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_bootstrap_wrapper_does_not_use_python_venv() -> None:
|
|
125
|
+
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
126
|
+
assert "python3 -m venv" not in script
|
|
127
|
+
assert "pip install" not in script
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_operator_docs_point_to_bootstrap_wrapper_not_legacy_install_script() -> None:
|
|
131
|
+
root = Path(__file__).resolve().parents[2]
|
|
132
|
+
docs = {
|
|
133
|
+
"README.md": (root / "README.md").read_text(),
|
|
134
|
+
"docs/guides/deployment.md": (root / "docs/guides/deployment.md").read_text(),
|
|
135
|
+
"docs/reference/environment.md": (root / "docs/reference/environment.md").read_text(),
|
|
136
|
+
}
|
|
137
|
+
for text in docs.values():
|
|
138
|
+
assert "scripts/install-agent.sh" in text
|
|
139
|
+
assert "bash agent/install.sh" not in text
|
|
140
|
+
assert "./agent/install.sh" not in text
|
|
141
|
+
assert "./agent/update.sh" not in text
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# Changelog — GridFleet Agent
|
|
2
|
-
|
|
3
|
-
All notable changes to the GridFleet host agent (`gridfleet-agent` on PyPI) are documented here.
|
|
4
|
-
|
|
5
|
-
## 0.2.0
|
|
6
|
-
|
|
7
|
-
### Features
|
|
8
|
-
|
|
9
|
-
- Rewrite bootstrap installer to use `uv tool install` instead of manual venv creation. Users no longer need Python 3.12+ pre-installed — `uv` handles it.
|
|
10
|
-
- Replace `validate_dedicated_venv` with `resolve_bin_path` — the agent no longer requires running from `/opt/gridfleet-agent/venv/bin/`. Supports `uv tool install` paths natively.
|
|
11
|
-
- Add `bin_path` to `InstallConfig` for configurable binary resolution in service unit templates (systemd/launchd).
|
|
12
|
-
- Replace `pip install --upgrade` with `uv tool upgrade gridfleet-agent` in the update flow.
|
|
13
|
-
- Add upgrade awareness: the agent caches version guidance from the manager's registration response and surfaces it on `/agent/health`, `HealthCheckResult.details`, and `gridfleet-agent status` CLI output.
|
|
14
|
-
- Use `importlib.metadata` for runtime version resolution — eliminates version sync issues between `pyproject.toml` and source.
|
|
15
|
-
|
|
16
|
-
### Fixes
|
|
17
|
-
|
|
18
|
-
- Update CLI tests for removed venv validation guard.
|
|
19
|
-
- Close port-allocator and adapter-loader race windows.
|
|
20
|
-
- Deduplicate and isolate tarball fetch targets.
|
|
21
|
-
- Hold `_start_lock` across `AppiumProcessManager.stop()` body.
|
|
22
|
-
- Authenticate agent driver-pack tarball fetches.
|
|
23
|
-
|
|
24
|
-
## 0.1.0 — Initial Public Preview
|
|
25
|
-
|
|
26
|
-
- Initial public preview of the GridFleet host agent.
|
|
27
|
-
- FastAPI agent that runs on each device host, spawning Appium processes and Selenium Grid relay nodes.
|
|
28
|
-
- Driver-pack runtime with manifest-driven adapter loading and isolated APPIUM_HOME.
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from stat import S_IXUSR
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def test_bootstrap_wrapper_uses_uv_tool_install() -> None:
|
|
6
|
-
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
7
|
-
assert script.startswith("#!/usr/bin/env bash")
|
|
8
|
-
assert "uv tool install" in script
|
|
9
|
-
assert "gridfleet-agent" in script
|
|
10
|
-
assert "--python 3.12" in script
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_bootstrap_wrapper_installs_uv_if_missing() -> None:
|
|
14
|
-
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
15
|
-
assert "astral.sh/uv/install.sh" in script
|
|
16
|
-
assert "command -v uv" in script
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_bootstrap_wrapper_calls_gridfleet_agent_install() -> None:
|
|
20
|
-
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
21
|
-
assert "gridfleet-agent install" in script
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_bootstrap_wrapper_is_executable() -> None:
|
|
25
|
-
script_path = Path(__file__).resolve().parents[2] / "scripts/install-agent.sh"
|
|
26
|
-
assert script_path.stat().st_mode & S_IXUSR
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def test_bootstrap_wrapper_supports_version_pinning() -> None:
|
|
30
|
-
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
31
|
-
assert "VERSION" in script
|
|
32
|
-
assert "gridfleet-agent==" in script
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def test_bootstrap_wrapper_defaults_to_start_mode() -> None:
|
|
36
|
-
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
37
|
-
assert "--start" in script
|
|
38
|
-
assert "--dry-run|--no-start|--start" in script
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_bootstrap_wrapper_does_not_use_python_venv() -> None:
|
|
42
|
-
script = (Path(__file__).resolve().parents[2] / "scripts/install-agent.sh").read_text()
|
|
43
|
-
assert "python3 -m venv" not in script
|
|
44
|
-
assert "pip install" not in script
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def test_operator_docs_point_to_bootstrap_wrapper_not_legacy_install_script() -> None:
|
|
48
|
-
root = Path(__file__).resolve().parents[2]
|
|
49
|
-
docs = {
|
|
50
|
-
"README.md": (root / "README.md").read_text(),
|
|
51
|
-
"docs/guides/deployment.md": (root / "docs/guides/deployment.md").read_text(),
|
|
52
|
-
"docs/reference/environment.md": (root / "docs/reference/environment.md").read_text(),
|
|
53
|
-
}
|
|
54
|
-
for text in docs.values():
|
|
55
|
-
assert "scripts/install-agent.sh" in text
|
|
56
|
-
assert "bash agent/install.sh" not in text
|
|
57
|
-
assert "./agent/install.sh" not in text
|
|
58
|
-
assert "./agent/update.sh" not in text
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_loader_cancel_path_leak.py
RENAMED
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_adapter_loader_concurrent_load.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_appium_process_integration.py
RENAMED
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_desired_manifest_features.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_pack_state_sidecar_reconcile.py
RENAMED
|
File without changes
|
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_runtime_isolated_failures.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_sidecar_supervisor_poll_stop_race.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/pack/test_tarball_fetch_concurrent_dedup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_port_alloc_race.py
RENAMED
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_restart_stop_race.py
RENAMED
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_stop_start_lock_race.py
RENAMED
|
File without changes
|
{gridfleet_agent-0.2.0 → gridfleet_agent-0.2.2}/tests/test_appium_process_watch_stop_race.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|