diff --git a/apps/api/src/api/tool/tool_instances.py b/apps/api/src/api/tool/tool_instances.py index 255e914..f82803e 100644 --- a/apps/api/src/api/tool/tool_instances.py +++ b/apps/api/src/api/tool/tool_instances.py @@ -92,6 +92,7 @@ from src.services.tool.instance_service import ( expand_glob_source, modify_compose_file, normalize_git_mount, + prepare_manifest_instance, pull_repository_updates, resolve_git_mount_mappings, resolve_git_mounts, @@ -104,6 +105,8 @@ from src.services.tool.instance_service import ( logger = logging.getLogger(__name__) _event_bus = InstanceEventBus() +router = APIRouter(prefix="/projects", tags=["tool-instances"]) + @router.post( "/{project_id}/repositories/{repo_id}/instances", @@ -257,6 +260,11 @@ async def create_instance( ssh_key_path = os.path.join(ssh_dir, "id_ed25519") # Clone repository + if not repo.remote_url: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Repository has no remote URL configured", + ) clone_path = clone_repository( remote_url=repo.remote_url, ssh_key_path=ssh_key_path, @@ -298,16 +306,16 @@ async def create_instance( # Create new local branch if requested if data.clone_mode == "clone" and data.new_branch: try: - result = subprocess.run( + git_result = subprocess.run( ["git", "-C", repo_path, "checkout", "-b", data.new_branch], capture_output=True, text=True, ) - if result.returncode != 0: + if git_result.returncode != 0: logger.error( - "Failed to create branch %s: %s", data.new_branch, result.stderr + "Failed to create branch %s: %s", data.new_branch, git_result.stderr ) - raise RuntimeError(f"Failed to create branch: {result.stderr}") + raise RuntimeError(f"Failed to create branch: {git_result.stderr}") logger.debug( "Created local branch %s in cloned repository", data.new_branch ) @@ -421,6 +429,11 @@ services: "USER_ID": str(user_id), "PROJECT_ID": str(project_id), } + if not tool_type.compose_template: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Tool type has no compose template configured", + ) compose_content = render_compose_template( tool_type.compose_template, variables ) @@ -1912,9 +1925,10 @@ async def check_instance_tunnel_health( # If container is not running, override error message if not container_healthy: - response["error"] = f"Container is {container_info['status']}" + error_msg = f"Container is {container_info['status']}" if container_info["exit_code"] is not None: - response["error"] += f" (exit code: {container_info['exit_code']})" + error_msg += f" (exit code: {container_info['exit_code']})" + response["error"] = error_msg return response diff --git a/apps/api/src/services/tool/instance_service.py b/apps/api/src/services/tool/instance_service.py index bfb8ab4..c2bf3ac 100644 --- a/apps/api/src/services/tool/instance_service.py +++ b/apps/api/src/services/tool/instance_service.py @@ -71,6 +71,7 @@ from src.auth.dependencies import _get_owned_project, _get_user logger = logging.getLogger(__name__) _event_bus = InstanceEventBus() + async def resolve_git_mounts( session: AsyncSession, resolved: ResolvedProfile, @@ -399,9 +400,6 @@ def expand_glob_source(source_path: str, repo_path: str) -> list[str]: return results -router = APIRouter(prefix="/projects", tags=["tool-instances"]) - - async def validate_config_profile( session: AsyncSession, profile_id: str | None, @@ -741,7 +739,6 @@ def ensure_backend_network_in_compose(compose_path: str) -> None: logger.info("Injected backend network '%s' into compose file", network_name) - async def prepare_manifest_instance( session: AsyncSession, instance: ToolInstance, @@ -882,5 +879,3 @@ async def prepare_manifest_instance( home_dir = get_manifest_home_dir(manifest) return image_tag, compose_content, manifest, home_dir - -