mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
16 Commits
feat/chat-
...
update-gro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ce89b4348 | ||
|
|
d23ee42b91 | ||
|
|
9c2b5039d2 | ||
|
|
c877d076ba | ||
|
|
75adcc48f5 | ||
|
|
347b45877d | ||
|
|
7512ffc16a | ||
|
|
aa545c29f1 | ||
|
|
54cdc5c744 | ||
|
|
a5afc1ff7a | ||
|
|
ecf23d3e74 | ||
|
|
c2e080a340 | ||
|
|
4b2ca6ca71 | ||
|
|
42d1a54670 | ||
|
|
25261673a1 | ||
|
|
d025d225ad |
@@ -58,34 +58,34 @@ RUN sed -i 's/^UID_MIN.*/UID_MIN 499/' /etc/login.defs
|
||||
# Default is 60000, but we've seen up to 200000
|
||||
RUN sed -i 's/^UID_MAX.*/UID_MAX 1000000/' /etc/login.defs
|
||||
|
||||
RUN groupadd --gid $OPENHANDS_USER_ID app
|
||||
RUN groupadd --gid $OPENHANDS_USER_ID openhands
|
||||
RUN useradd -l -m -u $OPENHANDS_USER_ID --gid $OPENHANDS_USER_ID -s /bin/bash openhands && \
|
||||
usermod -aG app openhands && \
|
||||
usermod -aG openhands openhands && \
|
||||
usermod -aG sudo openhands && \
|
||||
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
||||
RUN chown -R openhands:app /app && chmod -R 770 /app
|
||||
RUN sudo chown -R openhands:app $WORKSPACE_BASE && sudo chmod -R 770 $WORKSPACE_BASE
|
||||
RUN chown -R openhands:openhands /app && chmod -R 770 /app
|
||||
RUN sudo chown -R openhands:openhands $WORKSPACE_BASE && sudo chmod -R 770 $WORKSPACE_BASE
|
||||
USER openhands
|
||||
|
||||
ENV VIRTUAL_ENV=/app/.venv \
|
||||
PATH="/app/.venv/bin:$PATH" \
|
||||
PYTHONPATH='/app'
|
||||
|
||||
COPY --chown=openhands:app --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||
COPY --chown=openhands:openhands --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||
|
||||
COPY --chown=openhands:app --chmod=770 ./microagents ./microagents
|
||||
COPY --chown=openhands:app --chmod=770 ./openhands ./openhands
|
||||
COPY --chown=openhands:app --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
|
||||
COPY --chown=openhands:app pyproject.toml poetry.lock README.md MANIFEST.in LICENSE ./
|
||||
COPY --chown=openhands:openhands --chmod=770 ./microagents ./microagents
|
||||
COPY --chown=openhands:openhands --chmod=770 ./openhands ./openhands
|
||||
COPY --chown=openhands:openhands --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
|
||||
COPY --chown=openhands:openhands pyproject.toml poetry.lock README.md MANIFEST.in LICENSE ./
|
||||
|
||||
# This is run as "openhands" user, and will create __pycache__ with openhands:openhands ownership
|
||||
RUN python openhands/core/download.py # No-op to download assets
|
||||
# Add this line to set group ownership of all files/directories not already in "app" group
|
||||
# openhands:openhands -> openhands:app
|
||||
RUN find /app \! -group app -exec chgrp app {} +
|
||||
# openhands:openhands -> openhands:openhands
|
||||
RUN find /app \! -group openhands -exec chgrp openhands {} +
|
||||
|
||||
COPY --chown=openhands:app --chmod=770 --from=frontend-builder /app/build ./frontend/build
|
||||
COPY --chown=openhands:app --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh
|
||||
COPY --chown=openhands:openhands --chmod=770 --from=frontend-builder /app/build ./frontend/build
|
||||
COPY --chown=openhands:openhands --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -676,7 +676,9 @@ class ActionExecutor:
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.warning('Starting Action Execution Server')
|
||||
|
||||
logger.warning('Arguments passed to script:')
|
||||
for i, arg in enumerate(sys.argv):
|
||||
logger.warning(f'Argument {i}: {arg}')
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('port', type=int, help='Port to listen on')
|
||||
parser.add_argument('--working-dir', type=str, help='Working directory')
|
||||
|
||||
@@ -49,72 +49,124 @@ def init_user_and_working_directory(
|
||||
if username == os.getenv('USER') and username not in ['root', 'openhands']:
|
||||
return None
|
||||
|
||||
# Skip root since it is already created
|
||||
if username != 'root':
|
||||
# Check if the username already exists
|
||||
logger.info(f'Attempting to create user `{username}` with UID {user_id}.')
|
||||
existing_user_id = -1
|
||||
try:
|
||||
result = subprocess.run(
|
||||
f'id -u {username}', shell=True, check=True, capture_output=True
|
||||
)
|
||||
existing_user_id = int(result.stdout.decode().strip())
|
||||
|
||||
# The user ID already exists, skip setup
|
||||
if existing_user_id == user_id:
|
||||
logger.debug(
|
||||
f'User `{username}` already has the provided UID {user_id}. Skipping user setup.'
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f'User `{username}` already exists with UID {existing_user_id}. Skipping user setup.'
|
||||
)
|
||||
return existing_user_id
|
||||
return None
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Returncode 1 indicates, that the user does not exist yet
|
||||
if e.returncode == 1:
|
||||
logger.info(
|
||||
f'User `{username}` does not exist. Proceeding with user creation.'
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f'Error checking user `{username}`, skipping setup:\n{e}\n'
|
||||
)
|
||||
raise
|
||||
|
||||
# Add sudoer
|
||||
sudoer_line = r"echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
|
||||
output = subprocess.run(sudoer_line, shell=True, capture_output=True)
|
||||
if output.returncode != 0:
|
||||
raise RuntimeError(f'Failed to add sudoer: {output.stderr.decode()}')
|
||||
logger.debug(f'Added sudoer successfully. Output: [{output.stdout.decode()}]')
|
||||
|
||||
command = (
|
||||
f'useradd -rm -d /home/{username} -s /bin/bash '
|
||||
f'-g root -G sudo -u {user_id} {username}'
|
||||
)
|
||||
output = subprocess.run(command, shell=True, capture_output=True)
|
||||
if output.returncode == 0:
|
||||
logger.debug(
|
||||
f'Added user `{username}` successfully with UID {user_id}. Output: [{output.stdout.decode()}]'
|
||||
)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f'Failed to create user `{username}` with UID {user_id}. Output: [{output.stderr.decode()}]'
|
||||
)
|
||||
|
||||
# First create the working directory, independent of the user
|
||||
logger.debug(f'Client working directory: {initial_cwd}')
|
||||
command = f'umask 002; mkdir -p {initial_cwd}'
|
||||
output = subprocess.run(command, shell=True, capture_output=True)
|
||||
out_str = output.stdout.decode()
|
||||
logger.debug(f'mkdir command result: returncode={output.returncode}, stdout=[{out_str}], stderr=[{output.stderr.decode()}]')
|
||||
|
||||
command = f'chown -R {username}:root {initial_cwd}'
|
||||
# Check current ownership before changing it
|
||||
check_cmd = f'ls -la {initial_cwd}'
|
||||
check_output = subprocess.run(check_cmd, shell=True, capture_output=True)
|
||||
logger.debug(f'Current ownership: {check_output.stdout.decode()}')
|
||||
|
||||
# Check if we're running as root
|
||||
whoami_output = subprocess.run('whoami', shell=True, capture_output=True)
|
||||
current_user = whoami_output.stdout.decode().strip()
|
||||
logger.debug(f'Current user: {current_user}')
|
||||
|
||||
# Use sudo only if not running as root
|
||||
sudo_prefix = '' if current_user == 'root' else 'sudo '
|
||||
|
||||
command = f'{sudo_prefix}chown -R {username}:{username} {initial_cwd}'
|
||||
logger.debug(f'Executing chown command: {command}')
|
||||
output = subprocess.run(command, shell=True, capture_output=True)
|
||||
out_str += output.stdout.decode()
|
||||
logger.debug(f'chown command result: returncode={output.returncode}, stdout=[{output.stdout.decode()}], stderr=[{output.stderr.decode()}]')
|
||||
if output.returncode != 0 or output.stderr:
|
||||
err_str = output.stderr.decode()
|
||||
logger.error(f'chown command failed: returncode={output.returncode}, stderr: {err_str}')
|
||||
out_str += f' [stderr: {err_str}]'
|
||||
|
||||
command = f'chmod g+rw {initial_cwd}'
|
||||
command = f'{sudo_prefix}chmod g+rw {initial_cwd}'
|
||||
logger.debug(f'Executing chmod command: {command}')
|
||||
output = subprocess.run(command, shell=True, capture_output=True)
|
||||
out_str += output.stdout.decode()
|
||||
logger.debug(f'chmod command result: returncode={output.returncode}, stdout=[{output.stdout.decode()}], stderr=[{output.stderr.decode()}]')
|
||||
if output.returncode != 0 or output.stderr:
|
||||
err_str = output.stderr.decode()
|
||||
logger.error(f'chmod command failed: returncode={output.returncode}, stderr: {err_str}')
|
||||
out_str += f' [stderr: {err_str}]'
|
||||
|
||||
# Verify final ownership
|
||||
check_cmd = f'ls -la {initial_cwd}'
|
||||
check_output = subprocess.run(check_cmd, shell=True, capture_output=True)
|
||||
final_ownership = check_output.stdout.decode()
|
||||
logger.debug(f'Final ownership: {final_ownership}')
|
||||
|
||||
# If chown failed and directory is still owned by root, try alternative approaches
|
||||
if 'root root' in final_ownership and username != 'root':
|
||||
logger.warning(f'Directory {initial_cwd} is still owned by root, trying alternative approaches')
|
||||
|
||||
# Try to make it writable for the user's group
|
||||
alt_command = f'{sudo_prefix}chmod -R g+rwx {initial_cwd}'
|
||||
logger.debug(f'Executing alternative chmod command: {alt_command}')
|
||||
alt_output = subprocess.run(alt_command, shell=True, capture_output=True)
|
||||
logger.debug(f'Alternative chmod result: returncode={alt_output.returncode}, stderr=[{alt_output.stderr.decode()}]')
|
||||
|
||||
# Try to add the user to the root group (as a last resort)
|
||||
if alt_output.returncode != 0:
|
||||
group_command = f'{sudo_prefix}usermod -aG root {username}'
|
||||
logger.debug(f'Executing usermod command: {group_command}')
|
||||
group_output = subprocess.run(group_command, shell=True, capture_output=True)
|
||||
logger.debug(f'Usermod result: returncode={group_output.returncode}, stderr=[{group_output.stderr.decode()}]')
|
||||
|
||||
logger.debug(f'Created working directory. Output: [{out_str}]')
|
||||
|
||||
# Skip root since it is already created
|
||||
if username == 'root':
|
||||
return None
|
||||
|
||||
# Check if the username already exists
|
||||
existing_user_id = -1
|
||||
try:
|
||||
result = subprocess.run(
|
||||
f'id -u {username}', shell=True, check=True, capture_output=True
|
||||
)
|
||||
existing_user_id = int(result.stdout.decode().strip())
|
||||
|
||||
# The user ID already exists, skip setup
|
||||
if existing_user_id == user_id:
|
||||
logger.debug(
|
||||
f'User `{username}` already has the provided UID {user_id}. Skipping user setup.'
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f'User `{username}` already exists with UID {existing_user_id}. Skipping user setup.'
|
||||
)
|
||||
return existing_user_id
|
||||
return None
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Returncode 1 indicates, that the user does not exist yet
|
||||
if e.returncode == 1:
|
||||
logger.debug(
|
||||
f'User `{username}` does not exist. Proceeding with user creation.'
|
||||
)
|
||||
else:
|
||||
logger.error(f'Error checking user `{username}`, skipping setup:\n{e}\n')
|
||||
raise
|
||||
|
||||
# Add sudoer
|
||||
sudoer_line = r"echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
|
||||
output = subprocess.run(sudoer_line, shell=True, capture_output=True)
|
||||
if output.returncode != 0:
|
||||
raise RuntimeError(f'Failed to add sudoer: {output.stderr.decode()}')
|
||||
logger.debug(f'Added sudoer successfully. Output: [{output.stdout.decode()}]')
|
||||
|
||||
command = (
|
||||
f'useradd -rm -d /home/{username} -s /bin/bash '
|
||||
f'-g root -G sudo -u {user_id} {username}'
|
||||
)
|
||||
output = subprocess.run(command, shell=True, capture_output=True)
|
||||
if output.returncode == 0:
|
||||
logger.debug(
|
||||
f'Added user `{username}` successfully with UID {user_id}. Output: [{output.stdout.decode()}]'
|
||||
)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f'Failed to create user `{username}` with UID {user_id}. Output: [{output.stderr.decode()}]'
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -56,16 +56,8 @@ RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="/openhands/
|
||||
# Add /openhands/bin to PATH
|
||||
ENV PATH="/openhands/bin:${PATH}"
|
||||
|
||||
# Remove UID 1000 named pn or ubuntu, so the 'openhands' user can be created from ubuntu hosts
|
||||
RUN (if getent passwd 1000 | grep -q pn; then userdel pn; fi) && \
|
||||
(if getent passwd 1000 | grep -q ubuntu; then userdel ubuntu; fi)
|
||||
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /openhands && \
|
||||
mkdir -p /openhands/logs && \
|
||||
mkdir -p /openhands/poetry
|
||||
|
||||
|
||||
# ================================================================
|
||||
# Define Docker installation macro
|
||||
@@ -111,6 +103,29 @@ RUN \
|
||||
# Configure Docker daemon with MTU 1450 to prevent packet fragmentation issues
|
||||
RUN mkdir -p /etc/docker && \
|
||||
echo '{"mtu": 1450}' > /etc/docker/daemon.json
|
||||
|
||||
# Remove UID 1000 and GID 1000 users/groups that might conflict with openhands user
|
||||
RUN (if getent passwd 1000 | grep -q pn; then userdel pn; fi) && \
|
||||
(if getent passwd 1000 | grep -q ubuntu; then userdel ubuntu; fi) && \
|
||||
(if getent group 1000 | grep -q pn; then groupdel pn; fi) && \
|
||||
(if getent group 1000 | grep -q ubuntu; then groupdel ubuntu; fi)
|
||||
|
||||
# Create openhands group and user
|
||||
RUN groupadd -g 1000 openhands && \
|
||||
useradd -u 1000 -g 1000 -m -s /bin/bash openhands && \
|
||||
usermod -aG sudo openhands && \
|
||||
echo 'openhands ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /openhands && \
|
||||
mkdir -p /openhands/logs && \
|
||||
mkdir -p /openhands/poetry && \
|
||||
mkdir -p /workspace && \
|
||||
mkdir -p /workspace/.openhands && \
|
||||
mkdir -p /home/openhands/.openhands && \
|
||||
chown -R openhands:openhands /openhands && \
|
||||
chown -R openhands:openhands /workspace && \
|
||||
chown -R openhands:openhands /home/openhands
|
||||
{% endmacro %}
|
||||
|
||||
# Install Docker only if not a swebench or mswebench image
|
||||
@@ -150,7 +165,8 @@ RUN if [ -z "${RELEASE_TAG}" ]; then \
|
||||
if [ -d "${OPENVSCODE_SERVER_ROOT}" ]; then rm -rf "${OPENVSCODE_SERVER_ROOT}"; fi && \
|
||||
mv ${RELEASE_TAG}-linux-${arch} ${OPENVSCODE_SERVER_ROOT} && \
|
||||
cp ${OPENVSCODE_SERVER_ROOT}/bin/remote-cli/openvscode-server ${OPENVSCODE_SERVER_ROOT}/bin/remote-cli/code && \
|
||||
rm -f ${RELEASE_TAG}-linux-${arch}.tar.gz
|
||||
rm -f ${RELEASE_TAG}-linux-${arch}.tar.gz && \
|
||||
chown -R openhands:openhands ${OPENVSCODE_SERVER_ROOT}
|
||||
|
||||
|
||||
|
||||
@@ -159,10 +175,12 @@ RUN if [ -z "${RELEASE_TAG}" ]; then \
|
||||
{% macro install_vscode_extensions() %}
|
||||
# Install our custom extension
|
||||
RUN mkdir -p ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world && \
|
||||
cp -r /openhands/code/openhands/runtime/utils/vscode-extensions/hello-world/* ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world/
|
||||
cp -r /openhands/code/openhands/runtime/utils/vscode-extensions/hello-world/* ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world/ && \
|
||||
chown -R openhands:openhands ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world
|
||||
|
||||
RUN mkdir -p ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-memory-monitor && \
|
||||
cp -r /openhands/code/openhands/runtime/utils/vscode-extensions/memory-monitor/* ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-memory-monitor/
|
||||
cp -r /openhands/code/openhands/runtime/utils/vscode-extensions/memory-monitor/* ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-memory-monitor/ && \
|
||||
chown -R openhands:openhands ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-memory-monitor
|
||||
|
||||
# Some extension dirs are removed because they trigger false positives in vulnerability scans.
|
||||
RUN rm -rf ${OPENVSCODE_SERVER_ROOT}/extensions/{handlebars,pug,json,diff,grunt,ini,npm}
|
||||
@@ -185,9 +203,12 @@ RUN \
|
||||
{% endif %}
|
||||
# Set environment variables
|
||||
/openhands/micromamba/bin/micromamba run -n openhands poetry run python -c "import sys; print('OH_INTERPRETER_PATH=' + sys.executable)" >> /etc/environment && \
|
||||
# Set permissions
|
||||
# Set permissions and ownership
|
||||
chmod -R g+rws /openhands/poetry && \
|
||||
chown -R openhands:openhands /openhands/poetry && \
|
||||
mkdir -p /openhands/workspace && chmod -R g+rws,o+rw /openhands/workspace && \
|
||||
chown -R openhands:openhands /openhands/workspace && \
|
||||
chown -R openhands:openhands /openhands/micromamba && \
|
||||
# Clean up
|
||||
/openhands/micromamba/bin/micromamba run -n openhands poetry cache clear --all . -n && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
||||
@@ -208,7 +229,8 @@ RUN \
|
||||
RUN mkdir -p /openhands/micromamba/bin && \
|
||||
/bin/bash -c "PREFIX_LOCATION=/openhands/micromamba BIN_FOLDER=/openhands/micromamba/bin INIT_YES=no CONDA_FORGE_YES=yes $(curl -L https://micro.mamba.pm/install.sh)" && \
|
||||
/openhands/micromamba/bin/micromamba config remove channels defaults && \
|
||||
/openhands/micromamba/bin/micromamba config list
|
||||
/openhands/micromamba/bin/micromamba config list && \
|
||||
chown -R openhands:openhands /openhands/micromamba
|
||||
|
||||
# Create the openhands virtual environment and install poetry and python
|
||||
RUN /openhands/micromamba/bin/micromamba create -n openhands -y && \
|
||||
@@ -219,11 +241,12 @@ RUN \
|
||||
if [ -d /openhands/code ]; then rm -rf /openhands/code; fi && \
|
||||
mkdir -p /openhands/code/openhands && \
|
||||
touch /openhands/code/openhands/__init__.py && \
|
||||
chown -R openhands:openhands /openhands/code && \
|
||||
# Set global git configuration to ensure proper author/committer information
|
||||
git config --global user.name "openhands" && \
|
||||
git config --global user.email "openhands@all-hands.dev"
|
||||
|
||||
COPY ./code/pyproject.toml ./code/poetry.lock /openhands/code/
|
||||
COPY --chown=openhands:openhands ./code/pyproject.toml ./code/poetry.lock /openhands/code/
|
||||
|
||||
{{ install_dependencies() }}
|
||||
|
||||
@@ -234,14 +257,43 @@ COPY ./code/pyproject.toml ./code/poetry.lock /openhands/code/
|
||||
|
||||
{{ setup_vscode_server() }}
|
||||
|
||||
# ================================================================
|
||||
# Ensure openhands user and directories exist (for non-scratch builds)
|
||||
# ================================================================
|
||||
{% if not build_from_scratch %}
|
||||
# Remove UID 1000 and GID 1000 users/groups that might conflict with openhands user
|
||||
RUN (if getent passwd 1000 | grep -q pn; then userdel pn; fi) && \
|
||||
(if getent passwd 1000 | grep -q ubuntu; then userdel ubuntu; fi) && \
|
||||
(if getent group 1000 | grep -q pn; then groupdel pn; fi) && \
|
||||
(if getent group 1000 | grep -q ubuntu; then groupdel ubuntu; fi)
|
||||
|
||||
# Create openhands group and user if they don't exist
|
||||
RUN (getent group openhands || groupadd -g 1000 openhands) && \
|
||||
(getent passwd openhands || useradd -u 1000 -g 1000 -m -s /bin/bash openhands) && \
|
||||
usermod -aG sudo openhands && \
|
||||
echo 'openhands ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
||||
|
||||
# Create necessary directories and set ownership
|
||||
RUN mkdir -p /openhands && \
|
||||
mkdir -p /openhands/logs && \
|
||||
mkdir -p /openhands/poetry && \
|
||||
mkdir -p /workspace && \
|
||||
mkdir -p /workspace/.openhands && \
|
||||
mkdir -p /home/openhands/.openhands && \
|
||||
chown -R openhands:openhands /openhands && \
|
||||
chown -R openhands:openhands /workspace && \
|
||||
chown -R openhands:openhands /home/openhands
|
||||
{% endif %}
|
||||
|
||||
# ================================================================
|
||||
# Copy Project source files
|
||||
# ================================================================
|
||||
RUN if [ -d /openhands/code/openhands ]; then rm -rf /openhands/code/openhands; fi
|
||||
COPY ./code/pyproject.toml ./code/poetry.lock /openhands/code/
|
||||
COPY --chown=openhands:openhands ./code/pyproject.toml ./code/poetry.lock /openhands/code/
|
||||
|
||||
COPY ./code/openhands /openhands/code/openhands
|
||||
RUN chmod a+rwx /openhands/code/openhands/__init__.py
|
||||
COPY --chown=openhands:openhands ./code/openhands /openhands/code/openhands
|
||||
RUN chmod a+rwx /openhands/code/openhands/__init__.py && \
|
||||
chown -R openhands:openhands /openhands/code
|
||||
|
||||
|
||||
|
||||
@@ -255,3 +307,12 @@ RUN chmod a+rwx /openhands/code/openhands/__init__.py
|
||||
|
||||
# Install extra dependencies if specified
|
||||
{% if extra_deps %}RUN {{ extra_deps }} {% endif %}
|
||||
|
||||
# Copy entrypoint script and make it executable
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
|
||||
# Set the entrypoint to run as root first, then switch to openhands
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
# Note: We don't set USER openhands here because the entrypoint handles the user switch
|
||||
|
||||
32
openhands/runtime/utils/runtime_templates/entrypoint.sh
Executable file
32
openhands/runtime/utils/runtime_templates/entrypoint.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# This entrypoint script runs as root to fix workspace ownership before switching to openhands user
|
||||
|
||||
echo "🔧 OpenHands Runtime Entrypoint - Fixing workspace ownership..."
|
||||
|
||||
# Check if /workspace exists and fix ownership
|
||||
if [ -d "/workspace" ]; then
|
||||
echo "📁 Found /workspace directory, checking ownership..."
|
||||
ls -la /workspace
|
||||
|
||||
# Fix ownership to openhands:openhands
|
||||
echo "🔧 Changing ownership to openhands:openhands..."
|
||||
chown -R openhands:openhands /workspace
|
||||
chmod -R g+rw /workspace
|
||||
|
||||
echo "✅ Ownership fixed:"
|
||||
ls -la /workspace
|
||||
else
|
||||
echo "⚠️ /workspace directory not found, will be created later"
|
||||
fi
|
||||
|
||||
# If arguments are provided, execute them as the openhands user
|
||||
if [ $# -gt 0 ]; then
|
||||
echo "🚀 Switching to openhands user and executing: $@"
|
||||
# Use exec to replace the current process and preserve all arguments
|
||||
exec su openhands -c "exec \"\$@\"" -- "$@"
|
||||
else
|
||||
echo "🚀 Switching to openhands user with bash shell"
|
||||
exec su - openhands
|
||||
fi
|
||||
Reference in New Issue
Block a user