refactor(git): principled way to set git configuration for agents & re-enable git settings in UI (#10293)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Xingyao Wang
2025-08-13 16:45:15 -04:00
committed by GitHub
parent 6bdc5563cf
commit d256348a46
8 changed files with 83 additions and 71 deletions

View File

@@ -176,15 +176,11 @@ class ActionExecutor:
user_id: int,
enable_browser: bool,
browsergym_eval_env: str | None,
git_user_name: str = 'openhands',
git_user_email: str = 'openhands@all-hands.dev',
) -> None:
self.plugins_to_load = plugins_to_load
self._initial_cwd = work_dir
self.username = username
self.user_id = user_id
self.git_user_name = git_user_name
self.git_user_email = git_user_email
_updated_user_id = init_user_and_working_directory(
username=username, user_id=self.user_id, initial_cwd=work_dir
)
@@ -344,40 +340,11 @@ class ActionExecutor:
)
async def _init_bash_commands(self):
# You can add any bash commands you want to run on startup here
# It is empty because: Git configuration is now handled by the runtime client after connection
INIT_COMMANDS = []
is_local_runtime = os.environ.get('LOCAL_RUNTIME_MODE') == '1'
is_windows = sys.platform == 'win32'
# Determine git config commands based on platform and runtime mode
if is_local_runtime:
if is_windows:
# Windows, local - split into separate commands
INIT_COMMANDS.append(
f'git config --file ./.git_config user.name "{self.git_user_name}"'
)
INIT_COMMANDS.append(
f'git config --file ./.git_config user.email "{self.git_user_email}"'
)
INIT_COMMANDS.append(
'$env:GIT_CONFIG = (Join-Path (Get-Location) ".git_config")'
)
else:
INIT_COMMANDS.append(
f'git config --file ./.git_config user.name "{self.git_user_name}"'
)
INIT_COMMANDS.append(
f'git config --file ./.git_config user.email "{self.git_user_email}"'
)
INIT_COMMANDS.append('export GIT_CONFIG=$(pwd)/.git_config')
else:
# Non-local (implies Linux/macOS)
INIT_COMMANDS.append(
f'git config --global user.name "{self.git_user_name}"'
)
INIT_COMMANDS.append(
f'git config --global user.email "{self.git_user_email}"'
)
# Determine no-pager command
if is_windows:
no_pager_cmd = 'function git { git.exe --no-pager $args }'
@@ -696,18 +663,6 @@ if __name__ == '__main__':
help='BrowserGym environment used for browser evaluation',
default=None,
)
parser.add_argument(
'--git-user-name',
type=str,
help='Git user name for commits',
default='openhands',
)
parser.add_argument(
'--git-user-email',
type=str,
help='Git user email for commits',
default='openhands@all-hands.dev',
)
# example: python client.py 8000 --working-dir /workspace --plugins JupyterRequirement
args = parser.parse_args()
@@ -741,8 +696,6 @@ if __name__ == '__main__':
user_id=args.user_id,
enable_browser=args.enable_browser,
browsergym_eval_env=args.browsergym_eval_env,
git_user_name=args.git_user_name,
git_user_email=args.git_user_email,
)
await client.ainit()
logger.info('ActionExecutor initialized.')

View File

@@ -195,6 +195,9 @@ class Runtime(FileEditRuntimeMixin):
if self.config.sandbox.runtime_startup_env_vars:
self.add_env_vars(self.config.sandbox.runtime_startup_env_vars)
# Configure git settings
self._setup_git_config()
def close(self) -> None:
"""This should only be called by conversation manager or closing the session.
If called for instance by error handling, it could prevent recovery.
@@ -904,6 +907,42 @@ fi
async def connect(self) -> None:
pass
def _setup_git_config(self) -> None:
"""Configure git user settings during initial environment setup.
This method is called automatically during setup_initial_env() to ensure
git configuration is applied to the runtime environment.
"""
# Get git configuration from config
git_user_name = self.config.git_user_name
git_user_email = self.config.git_user_email
# Skip git configuration for CLI runtime to preserve user's local git settings
is_cli_runtime = self.config.runtime == 'cli'
if is_cli_runtime:
logger.debug(
"Skipping git configuration for CLI runtime - using user's local git config"
)
return
# All runtimes (except CLI) use global git config
cmd = f'git config --global user.name "{git_user_name}" && git config --global user.email "{git_user_email}"'
# Execute git configuration command
try:
action = CmdRunAction(command=cmd)
obs = self.run(action)
if isinstance(obs, CmdOutputObservation) and obs.exit_code != 0:
logger.warning(
f'Git config command failed: {cmd}, error: {obs.content}'
)
else:
logger.info(
f'Successfully configured git: name={git_user_name}, email={git_user_email}'
)
except Exception as e:
logger.warning(f'Failed to execute git config command: {cmd}, error: {e}')
@abstractmethod
def get_mcp_config(
self, extra_stdio_servers: list[MCPStdioServerConfig] | None = None

View File

@@ -57,10 +57,6 @@ def get_action_execution_server_startup_command(
username,
'--user-id',
str(user_id),
'--git-user-name',
app_config.git_user_name,
'--git-user-email',
app_config.git_user_email,
*browsergym_args,
]