Skip to content

Commit 4acf67b

Browse files
authored
Merge pull request #185 from meta-pytorch/fix-standard-template-for-spaces
[FIX] make cli push compatible with hf spaces
2 parents 12eab8a + caffcbc commit 4acf67b

File tree

4 files changed

+47
-15
lines changed

4 files changed

+47
-15
lines changed

src/openenv_cli/commands/push.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -138,38 +138,64 @@ def _prepare_staging_directory(
138138
else:
139139
shutil.copy2(item, dest)
140140

141+
# Ensure Dockerfile is at repository root (required by Hugging Face)
142+
dockerfile_server_path = staging_dir / "server" / "Dockerfile"
143+
dockerfile_root_path = staging_dir / "Dockerfile"
144+
dockerfile_path: Path | None = None
145+
146+
if dockerfile_server_path.exists():
147+
if dockerfile_root_path.exists():
148+
dockerfile_root_path.unlink()
149+
dockerfile_server_path.rename(dockerfile_root_path)
150+
console.print(
151+
"[bold cyan]Moved Dockerfile to repository root for deployment[/bold cyan]"
152+
)
153+
dockerfile_path = dockerfile_root_path
154+
elif dockerfile_root_path.exists():
155+
dockerfile_path = dockerfile_root_path
156+
141157
# Modify Dockerfile to optionally enable web interface and update base image
142-
dockerfile_path = staging_dir / "server" / "Dockerfile"
143-
if dockerfile_path.exists():
158+
if dockerfile_path and dockerfile_path.exists():
144159
dockerfile_content = dockerfile_path.read_text()
145160
lines = dockerfile_content.split("\n")
146161
new_lines = []
147162
cmd_found = False
148163
base_image_updated = False
149164
web_interface_env_exists = "ENABLE_WEB_INTERFACE" in dockerfile_content
165+
last_instruction = None
150166

151167
for line in lines:
168+
stripped = line.strip()
169+
token = stripped.split(maxsplit=1)[0] if stripped else ""
170+
current_instruction = token.upper()
171+
172+
is_healthcheck_continuation = last_instruction == "HEALTHCHECK"
173+
152174
# Update base image if specified
153-
if base_image and line.strip().startswith("FROM") and not base_image_updated:
154-
# Replace the FROM line with new base image
175+
if base_image and stripped.startswith("FROM") and not base_image_updated:
155176
new_lines.append(f"FROM {base_image}")
156177
base_image_updated = True
178+
last_instruction = "FROM"
157179
continue
158180

159-
# Add ENABLE_WEB_INTERFACE before CMD if requested
160-
if line.strip().startswith("CMD") and not cmd_found and not web_interface_env_exists:
161-
# Add ENV line before CMD
162-
if enable_interface:
163-
new_lines.append("ENV ENABLE_WEB_INTERFACE=true")
181+
if (
182+
stripped.startswith("CMD")
183+
and not cmd_found
184+
and not web_interface_env_exists
185+
and enable_interface
186+
and not is_healthcheck_continuation
187+
):
188+
new_lines.append("ENV ENABLE_WEB_INTERFACE=true")
164189
cmd_found = True
165190

166191
new_lines.append(line)
167192

168-
# Add ENABLE_WEB_INTERFACE if CMD not found and not already present
193+
if current_instruction:
194+
last_instruction = current_instruction
195+
169196
if not cmd_found and not web_interface_env_exists and enable_interface:
170197
new_lines.append("ENV ENABLE_WEB_INTERFACE=true")
171198

172-
# If base image was specified but FROM line wasn't found, add it at the beginning
173199
if base_image and not base_image_updated:
174200
new_lines.insert(0, f"FROM {base_image}")
175201

src/openenv_cli/templates/openenv_env/server/Dockerfile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# - Standalone environments (with openenv-core from pip)
1111
# The build script (openenv build) handles context detection and sets appropriate build args.
1212

13-
ARG BASE_IMAGE=openenv-base:latest
13+
ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest
1414
FROM ${BASE_IMAGE} AS builder
1515

1616
WORKDIR /app
@@ -26,6 +26,13 @@ COPY . /app/env
2626
# For standalone builds, openenv-core will be installed from pip via pyproject.toml
2727
WORKDIR /app/env
2828

29+
# Ensure uv is available (for local builds where base image lacks it)
30+
RUN if ! command -v uv >/dev/null 2>&1; then \
31+
curl -LsSf https://astral.sh/uv/install.sh | sh && \
32+
mv /root/.local/bin/uv /usr/local/bin/uv && \
33+
mv /root/.local/bin/uvx /usr/local/bin/uvx; \
34+
fi
35+
2936
# Install dependencies using uv sync
3037
# If uv.lock exists, use it; otherwise resolve on the fly
3138
RUN --mount=type=cache,target=/root/.cache/uv \

src/openenv_cli/templates/openenv_env/server/__ENV_NAME___environment.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from openenv_core.env_server.interfaces import Environment
1717
from openenv_core.env_server.types import State
1818

19-
from __ENV_NAME__.models import __ENV_CLASS_NAME__Action, __ENV_CLASS_NAME__Observation
19+
from models import __ENV_CLASS_NAME__Action, __ENV_CLASS_NAME__Observation
2020

2121

2222
class __ENV_CLASS_NAME__Environment(Environment):
@@ -93,4 +93,3 @@ def state(self) -> State:
9393
Current State with episode_id and step_count
9494
"""
9595
return self._state
96-

src/openenv_cli/templates/openenv_env/server/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
) from e
3131

3232
from .__ENV_NAME___environment import __ENV_CLASS_NAME__Environment
33-
from __ENV_NAME__.models import __ENV_CLASS_NAME__Action, __ENV_CLASS_NAME__Observation
33+
from models import __ENV_CLASS_NAME__Action, __ENV_CLASS_NAME__Observation
3434

3535
# Create the environment instance
3636
env = __ENV_CLASS_NAME__Environment()

0 commit comments

Comments
 (0)