mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Fix long setup scripts failing (#8101)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
998e04e51b
commit
8a5c6d3bef
@ -100,9 +100,8 @@ def initialize_repository_for_runtime(
|
||||
The repository directory path if a repository was cloned, None otherwise.
|
||||
"""
|
||||
# clone selected repository if provided
|
||||
github_token = (
|
||||
SecretStr(os.environ.get('GITHUB_TOKEN')) if not github_token else github_token
|
||||
)
|
||||
if github_token is None and 'GITHUB_TOKEN' in os.environ:
|
||||
github_token = SecretStr(os.environ['GITHUB_TOKEN'])
|
||||
|
||||
secret_store = (
|
||||
SecretStore(
|
||||
|
||||
@ -16,11 +16,9 @@ class CmdRunAction(Action):
|
||||
)
|
||||
is_input: bool = False # if True, the command is an input to the running process
|
||||
thought: str = ''
|
||||
blocking: bool = False
|
||||
blocking: bool = False # if True, the command will be run in a blocking manner, but a timeout must be set through _set_hard_timeout
|
||||
is_static: bool = False # if True, runs the command in a separate process
|
||||
cwd: str | None = None # current working directory, only used if is_static is True
|
||||
# If blocking is True, the command will be run in a blocking manner.
|
||||
# e.g., it will NOT return early due to soft timeout.
|
||||
hidden: bool = False
|
||||
action: str = ActionType.RUN
|
||||
runnable: ClassVar[bool] = True
|
||||
|
||||
@ -97,7 +97,7 @@ class Runtime(FileEditRuntimeMixin):
|
||||
config: AppConfig
|
||||
initial_env_vars: dict[str, str]
|
||||
attach_to_existing: bool
|
||||
status_callback: Callable | None
|
||||
status_callback: Callable[[str, str, str], None] | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -106,7 +106,7 @@ class Runtime(FileEditRuntimeMixin):
|
||||
sid: str = 'default',
|
||||
plugins: list[PluginRequirement] | None = None,
|
||||
env_vars: dict[str, str] | None = None,
|
||||
status_callback: Callable | None = None,
|
||||
status_callback: Callable[[str, str, str], None] | None = None,
|
||||
attach_to_existing: bool = False,
|
||||
headless_mode: bool = False,
|
||||
user_id: str | None = None,
|
||||
@ -344,12 +344,6 @@ class Runtime(FileEditRuntimeMixin):
|
||||
else selected_repository.git_provider
|
||||
)
|
||||
|
||||
if not git_provider_tokens:
|
||||
raise RuntimeError('Need git provider tokens to clone repo')
|
||||
git_token = git_provider_tokens[chosen_provider].token
|
||||
if not git_token:
|
||||
raise RuntimeError('Need a valid git token to clone repo')
|
||||
|
||||
domain = provider_domains[chosen_provider]
|
||||
repository = (
|
||||
selected_repository
|
||||
@ -357,12 +351,18 @@ class Runtime(FileEditRuntimeMixin):
|
||||
else selected_repository.full_name
|
||||
)
|
||||
|
||||
if chosen_provider == ProviderType.GITLAB:
|
||||
remote_repo_url = f'https://oauth2:{git_token.get_secret_value()}@{domain}/{repository}.git'
|
||||
# Try to use token if available, otherwise use public URL
|
||||
if git_provider_tokens and chosen_provider in git_provider_tokens:
|
||||
git_token = git_provider_tokens[chosen_provider].token
|
||||
if git_token:
|
||||
if chosen_provider == ProviderType.GITLAB:
|
||||
remote_repo_url = f'https://oauth2:{git_token.get_secret_value()}@{domain}/{repository}.git'
|
||||
else:
|
||||
remote_repo_url = f'https://{git_token.get_secret_value()}@{domain}/{repository}.git'
|
||||
else:
|
||||
remote_repo_url = f'https://{domain}/{repository}.git'
|
||||
else:
|
||||
remote_repo_url = (
|
||||
f'https://{git_token.get_secret_value()}@{domain}/{repository}.git'
|
||||
)
|
||||
remote_repo_url = f'https://{domain}/{repository}.git'
|
||||
|
||||
if not remote_repo_url:
|
||||
raise ValueError('Missing either Git token or valid repository')
|
||||
@ -409,7 +409,11 @@ class Runtime(FileEditRuntimeMixin):
|
||||
'info', 'STATUS$SETTING_UP_WORKSPACE', 'Setting up workspace...'
|
||||
)
|
||||
|
||||
action = CmdRunAction(f'chmod +x {setup_script} && source {setup_script}')
|
||||
# setup scripts time out after 10 minutes
|
||||
action = CmdRunAction(
|
||||
f'chmod +x {setup_script} && source {setup_script}', blocking=True
|
||||
)
|
||||
action.set_hard_timeout(600)
|
||||
obs = self.run_action(action)
|
||||
if isinstance(obs, CmdOutputObservation) and obs.exit_code != 0:
|
||||
self.log('error', f'Setup script failed: {obs.content}')
|
||||
|
||||
@ -253,6 +253,8 @@ class ActionExecutionClient(Runtime):
|
||||
|
||||
# set timeout to default if not set
|
||||
if action.timeout is None:
|
||||
if isinstance(action, CmdRunAction) and action.blocking:
|
||||
raise RuntimeError('Blocking command with no timeout set')
|
||||
# We don't block the command if this is a default timeout action
|
||||
action.set_hard_timeout(self.config.sandbox.timeout, blocking=False)
|
||||
|
||||
|
||||
69
tests/runtime/test_setup.py
Normal file
69
tests/runtime/test_setup.py
Normal file
@ -0,0 +1,69 @@
|
||||
"""Tests for the setup script."""
|
||||
|
||||
from conftest import (
|
||||
_load_runtime,
|
||||
)
|
||||
|
||||
from openhands.core.setup import initialize_repository_for_runtime
|
||||
from openhands.events.action import FileReadAction, FileWriteAction
|
||||
from openhands.events.observation import FileReadObservation, FileWriteObservation
|
||||
|
||||
|
||||
def test_initialize_repository_for_runtime(temp_dir, runtime_cls, run_as_openhands):
|
||||
"""Test that the initialize_repository_for_runtime function works."""
|
||||
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
|
||||
repository_dir = initialize_repository_for_runtime(
|
||||
runtime, 'https://github.com/All-Hands-AI/OpenHands'
|
||||
)
|
||||
assert repository_dir is not None
|
||||
assert repository_dir == 'OpenHands'
|
||||
|
||||
|
||||
def test_maybe_run_setup_script(temp_dir, runtime_cls, run_as_openhands):
|
||||
"""Test that setup script is executed when it exists."""
|
||||
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
|
||||
|
||||
setup_script = '.openhands/setup.sh'
|
||||
write_obs = runtime.write(
|
||||
FileWriteAction(
|
||||
path=setup_script, content="#!/bin/bash\necho 'Hello World' >> README.md\n"
|
||||
)
|
||||
)
|
||||
assert isinstance(write_obs, FileWriteObservation)
|
||||
|
||||
# Run setup script
|
||||
runtime.maybe_run_setup_script()
|
||||
|
||||
# Verify script was executed by checking output
|
||||
read_obs = runtime.read(FileReadAction(path='README.md'))
|
||||
assert isinstance(read_obs, FileReadObservation)
|
||||
assert read_obs.content == 'Hello World\n'
|
||||
|
||||
|
||||
def test_maybe_run_setup_script_with_long_timeout(
|
||||
temp_dir, runtime_cls, run_as_openhands
|
||||
):
|
||||
"""Test that setup script is executed when it exists."""
|
||||
runtime, config = _load_runtime(
|
||||
temp_dir,
|
||||
runtime_cls,
|
||||
run_as_openhands,
|
||||
runtime_startup_env_vars={'NO_CHANGE_TIMEOUT_SECONDS': '1'},
|
||||
)
|
||||
|
||||
setup_script = '.openhands/setup.sh'
|
||||
write_obs = runtime.write(
|
||||
FileWriteAction(
|
||||
path=setup_script,
|
||||
content="#!/bin/bash\nsleep 3 && echo 'Hello World' >> README.md\n",
|
||||
)
|
||||
)
|
||||
assert isinstance(write_obs, FileWriteObservation)
|
||||
|
||||
# Run setup script
|
||||
runtime.maybe_run_setup_script()
|
||||
|
||||
# Verify script was executed by checking output
|
||||
read_obs = runtime.read(FileReadAction(path='README.md'))
|
||||
assert isinstance(read_obs, FileReadObservation)
|
||||
assert read_obs.content == 'Hello World\n'
|
||||
Loading…
x
Reference in New Issue
Block a user