|
@@ -1,8 +1,8 @@
|
|
|
import argparse
|
|
import argparse
|
|
|
import hashlib
|
|
import hashlib
|
|
|
|
|
+import importlib.metadata
|
|
|
import os
|
|
import os
|
|
|
import shutil
|
|
import shutil
|
|
|
-import subprocess
|
|
|
|
|
import tempfile
|
|
import tempfile
|
|
|
|
|
|
|
|
import docker
|
|
import docker
|
|
@@ -10,6 +10,7 @@ from dirhash import dirhash
|
|
|
from jinja2 import Environment, FileSystemLoader
|
|
from jinja2 import Environment, FileSystemLoader
|
|
|
|
|
|
|
|
import openhands
|
|
import openhands
|
|
|
|
|
+from openhands import __package_name__
|
|
|
from openhands import __version__ as oh_version
|
|
from openhands import __version__ as oh_version
|
|
|
from openhands.core.logger import openhands_logger as logger
|
|
from openhands.core.logger import openhands_logger as logger
|
|
|
from openhands.runtime.builder import DockerRuntimeBuilder, RuntimeBuilder
|
|
from openhands.runtime.builder import DockerRuntimeBuilder, RuntimeBuilder
|
|
@@ -22,55 +23,50 @@ def get_runtime_image_repo():
|
|
|
def _put_source_code_to_dir(temp_dir: str):
|
|
def _put_source_code_to_dir(temp_dir: str):
|
|
|
"""Builds the project source tarball directly in temp_dir and unpacks it.
|
|
"""Builds the project source tarball directly in temp_dir and unpacks it.
|
|
|
The OpenHands source code ends up in the temp_dir/code directory.
|
|
The OpenHands source code ends up in the temp_dir/code directory.
|
|
|
-
|
|
|
|
|
Parameters:
|
|
Parameters:
|
|
|
- temp_dir (str): The directory to put the source code in
|
|
- temp_dir (str): The directory to put the source code in
|
|
|
"""
|
|
"""
|
|
|
if not os.path.isdir(temp_dir):
|
|
if not os.path.isdir(temp_dir):
|
|
|
raise RuntimeError(f'Temp directory {temp_dir} does not exist')
|
|
raise RuntimeError(f'Temp directory {temp_dir} does not exist')
|
|
|
|
|
|
|
|
- project_root = os.path.dirname(os.path.dirname(os.path.abspath(openhands.__file__)))
|
|
|
|
|
- logger.info(f'Building source distribution using project root: {project_root}')
|
|
|
|
|
-
|
|
|
|
|
- # Fetch the correct version from pyproject.toml
|
|
|
|
|
- package_version = oh_version
|
|
|
|
|
- tarball_filename = f'openhands_ai-{package_version}.tar.gz'
|
|
|
|
|
- tarball_path = os.path.join(temp_dir, tarball_filename)
|
|
|
|
|
-
|
|
|
|
|
- # Run "python -m build -s" on project_root to create project tarball directly in temp_dir
|
|
|
|
|
- _cleaned_project_root = project_root.replace(
|
|
|
|
|
- ' ', r'\ '
|
|
|
|
|
- ) # escape spaces in the project root
|
|
|
|
|
- result = subprocess.run(
|
|
|
|
|
- f'python -m build -s -o "{temp_dir}" {_cleaned_project_root}',
|
|
|
|
|
- shell=True,
|
|
|
|
|
- stdout=subprocess.PIPE,
|
|
|
|
|
- stderr=subprocess.PIPE,
|
|
|
|
|
- )
|
|
|
|
|
- logger.info(result.stdout.decode())
|
|
|
|
|
- err_logs = result.stderr.decode()
|
|
|
|
|
- if err_logs:
|
|
|
|
|
- logger.error(err_logs)
|
|
|
|
|
-
|
|
|
|
|
- if result.returncode != 0:
|
|
|
|
|
- logger.error(f'Image build failed:\n{result}')
|
|
|
|
|
- raise RuntimeError(f'Image build failed:\n{result}')
|
|
|
|
|
-
|
|
|
|
|
- if not os.path.exists(tarball_path):
|
|
|
|
|
- logger.error(f'Source distribution not found at {tarball_path}. (Do you need to run `make build`?)')
|
|
|
|
|
- raise RuntimeError(f'Source distribution not found at {tarball_path}')
|
|
|
|
|
- logger.info(f'Source distribution created at {tarball_path}')
|
|
|
|
|
-
|
|
|
|
|
- # Unzip the tarball
|
|
|
|
|
- shutil.unpack_archive(tarball_path, temp_dir)
|
|
|
|
|
- # Remove the tarball
|
|
|
|
|
- os.remove(tarball_path)
|
|
|
|
|
- # Rename the directory containing the code to 'code'
|
|
|
|
|
- os.rename(
|
|
|
|
|
- os.path.join(temp_dir, f'openhands_ai-{package_version}'),
|
|
|
|
|
- os.path.join(temp_dir, 'code'),
|
|
|
|
|
- )
|
|
|
|
|
- logger.info(f'Unpacked source code directory: {os.path.join(temp_dir, "code")}')
|
|
|
|
|
|
|
+ dest_dir = os.path.join(temp_dir, 'code')
|
|
|
|
|
+ openhands_dir = None
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ # Try to get the source directory from the installed package
|
|
|
|
|
+ distribution = importlib.metadata.distribution(__package_name__)
|
|
|
|
|
+ source_dir = os.path.dirname(distribution.locate_file(__package_name__))
|
|
|
|
|
+ openhands_dir = os.path.join(source_dir, 'openhands')
|
|
|
|
|
+ except importlib.metadata.PackageNotFoundError:
|
|
|
|
|
+ pass
|
|
|
|
|
+
|
|
|
|
|
+ if openhands_dir is not None and os.path.isdir(openhands_dir):
|
|
|
|
|
+ logger.info(f'Package {__package_name__} found')
|
|
|
|
|
+ shutil.copytree(openhands_dir, os.path.join(dest_dir, 'openhands'))
|
|
|
|
|
+ # note: "pyproject.toml" and "poetry.lock" are included in the openhands
|
|
|
|
|
+ # package, so we need to move them out to the top-level directory
|
|
|
|
|
+ for filename in ['pyproject.toml', 'poetry.lock']:
|
|
|
|
|
+ shutil.move(os.path.join(dest_dir, 'openhands', filename), dest_dir)
|
|
|
|
|
+ else:
|
|
|
|
|
+ # If package is not found, build from source code
|
|
|
|
|
+ project_root = os.path.dirname(
|
|
|
|
|
+ os.path.dirname(os.path.abspath(openhands.__file__))
|
|
|
|
|
+ )
|
|
|
|
|
+ logger.info(f'Building source distribution using project root: {project_root}')
|
|
|
|
|
+
|
|
|
|
|
+ # Copy the 'openhands' directory
|
|
|
|
|
+ openhands_dir = os.path.join(project_root, 'openhands')
|
|
|
|
|
+ if not os.path.isdir(openhands_dir):
|
|
|
|
|
+ raise RuntimeError(f"'openhands' directory not found in {project_root}")
|
|
|
|
|
+ shutil.copytree(openhands_dir, os.path.join(dest_dir, 'openhands'))
|
|
|
|
|
+
|
|
|
|
|
+ # Copy pyproject.toml and poetry.lock files
|
|
|
|
|
+ for file in ['pyproject.toml', 'poetry.lock']:
|
|
|
|
|
+ src_file = os.path.join(project_root, file)
|
|
|
|
|
+ dest_file = os.path.join(dest_dir, file)
|
|
|
|
|
+ shutil.copy2(src_file, dest_file)
|
|
|
|
|
+
|
|
|
|
|
+ logger.info(f'Unpacked source code directory: {dest_dir}')
|
|
|
|
|
|
|
|
|
|
|
|
|
def _generate_dockerfile(
|
|
def _generate_dockerfile(
|