Architect8999 commited on
Commit
8a4157c
·
verified ·
1 Parent(s): 846ce24

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +15 -11
  2. app.py +21 -2
  3. language_runtime.py +29 -1
Dockerfile CHANGED
@@ -10,17 +10,16 @@ RUN apt-get update && \
10
  apt-get install -y --no-install-recommends git curl ca-certificates build-essential && \
11
  rm -rf /var/lib/apt/lists/*
12
 
13
- # Fix: Use the official Astral image for a complete, clean uv installation
14
  COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
15
 
16
  WORKDIR /build
17
  COPY requirements.txt .
18
 
19
- # [span_1](start_span)FIX: Use uv to resolve the entire requirements tree at once.[span_1](end_span)
20
- # [span_2](start_span)This prevents the "ResolutionImpossible" error by finding the[span_2](end_span)
21
- # [span_3](start_span)valid intersection between Gradio 5 and Aider-chat.[span_3](end_span)
22
- RUN uv pip install --no-cache --system -r requirements.txt mcp-server-fetch && \
23
- pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt mcp-server-fetch
24
 
25
 
26
  # Stage 2: Runtime
@@ -28,7 +27,10 @@ FROM python:3.12-slim AS runtime
28
 
29
  LABEL org.opencontainers.image.title="Rhodawk AI DevSecOps Engine"
30
 
31
- # Added critical uv environment variables to force system Python
 
 
 
32
  ENV DEBIAN_FRONTEND=noninteractive \
33
  PYTHONUNBUFFERED=1 \
34
  GRADIO_SERVER_NAME=0.0.0.0 \
@@ -37,7 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \
37
  PATH="/home/rhodawk/.local/bin:/usr/local/bin:$PATH" \
38
  UV_LINK_MODE=copy \
39
  UV_PYTHON_PREFERENCE=system \
40
- UV_PYTHON=/usr/local/bin/python
41
 
42
  RUN apt-get update && \
43
  apt-get install -y --no-install-recommends git curl ca-certificates nodejs npm && \
@@ -45,18 +47,20 @@ RUN apt-get update && \
45
 
46
  RUN npm install -g --quiet @modelcontextprotocol/server-github
47
 
48
- # Fix: Hugging Face UID 1000 handling
49
  RUN id -u 1000 >/dev/null 2>&1 && (userdel -r $(id -un 1000) || true) || true && \
50
  useradd -m -u 1000 -s /bin/bash rhodawk
51
 
52
- RUN mkdir -p /data /app && chown -R rhodawk:rhodawk /data /app
 
 
53
 
54
  WORKDIR /app
55
 
56
  # Copy pre-built wheels from builder
57
  COPY --from=builder /wheels /wheels
58
 
59
- # ADDED FIX: Copy the uv executable from the official image
60
  COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
61
 
62
  RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
 
10
  apt-get install -y --no-install-recommends git curl ca-certificates build-essential && \
11
  rm -rf /var/lib/apt/lists/*
12
 
13
+ # Use the official Astral image for a complete, clean uv installation
14
  COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
15
 
16
  WORKDIR /build
17
  COPY requirements.txt .
18
 
19
+ # FIX: only build wheels here do NOT also run uv pip install --system.
20
+ # The previous double-install (uv pip install --system AND pip wheel) was
21
+ # redundant and could produce conflicting bytecode in the builder layer.
22
+ RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt mcp-server-fetch
 
23
 
24
 
25
  # Stage 2: Runtime
 
27
 
28
  LABEL org.opencontainers.image.title="Rhodawk AI DevSecOps Engine"
29
 
30
+ # FIX: UV_PYTHON now points to python3 (always present in python:3.12-slim)
31
+ # rather than /usr/local/bin/python which may lack the executable in some
32
+ # HuggingFace Space runtime snapshots. UV_PYTHON_PREFERENCE=system tells uv
33
+ # to skip its managed-toolchain download and use the container Python directly.
34
  ENV DEBIAN_FRONTEND=noninteractive \
35
  PYTHONUNBUFFERED=1 \
36
  GRADIO_SERVER_NAME=0.0.0.0 \
 
39
  PATH="/home/rhodawk/.local/bin:/usr/local/bin:$PATH" \
40
  UV_LINK_MODE=copy \
41
  UV_PYTHON_PREFERENCE=system \
42
+ UV_PYTHON=/usr/local/bin/python3
43
 
44
  RUN apt-get update && \
45
  apt-get install -y --no-install-recommends git curl ca-certificates nodejs npm && \
 
47
 
48
  RUN npm install -g --quiet @modelcontextprotocol/server-github
49
 
50
+ # Hugging Face UID 1000 handling
51
  RUN id -u 1000 >/dev/null 2>&1 && (userdel -r $(id -un 1000) || true) || true && \
52
  useradd -m -u 1000 -s /bin/bash rhodawk
53
 
54
+ # FIX: create /data with explicit mode so uv venv can write target_venv
55
+ # even before the application calls os.makedirs() at runtime.
56
+ RUN mkdir -p /data /app && chmod 777 /data && chown -R rhodawk:rhodawk /app
57
 
58
  WORKDIR /app
59
 
60
  # Copy pre-built wheels from builder
61
  COPY --from=builder /wheels /wheels
62
 
63
+ # Copy the uv executable from the official image
64
  COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
65
 
66
  RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
app.py CHANGED
@@ -306,16 +306,35 @@ def get_changed_files() -> list[str]:
306
 
307
 
308
  def setup_target_venv() -> str:
 
 
 
 
 
309
  if not os.path.exists(VENV_DIR):
310
  ui_log("Creating isolated virtualenv via uv...")
311
- run_subprocess_safe(["uv", "venv", VENV_DIR], cwd="/tmp")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  pytest_bin = os.path.join(VENV_DIR, "bin", "pytest")
313
  req_path = os.path.join(REPO_DIR, "requirements.txt")
314
  if os.path.exists(req_path):
315
  ui_log("Installing target repo deps via uv...")
316
  run_subprocess_safe(
317
  ["uv", "pip", "install", "--python", VENV_DIR, "--quiet", "-r", req_path],
318
- cwd=REPO_DIR, timeout=600,
319
  )
320
  return pytest_bin
321
 
 
306
 
307
 
308
  def setup_target_venv() -> str:
309
+ import sys as _sys
310
+
311
+ # FIX: ensure /data (PERSISTENT_DIR) exists before writing into it.
312
+ os.makedirs(PERSISTENT_DIR, exist_ok=True)
313
+
314
  if not os.path.exists(VENV_DIR):
315
  ui_log("Creating isolated virtualenv via uv...")
316
+ # FIX: pass --python explicitly; without it uv exits 2 on Space restarts
317
+ # when its managed-Python cache is cold or UV_PYTHON is unresolvable.
318
+ _, code = run_subprocess_safe(
319
+ ["uv", "venv", "--python", _sys.executable, VENV_DIR],
320
+ cwd="/tmp", raise_on_error=False,
321
+ )
322
+ if code != 0:
323
+ # FIX: fallback to stdlib venv so the audit can continue even when
324
+ # uv's Python resolution fails inside the container.
325
+ ui_log(f"⚠️ uv venv failed (exit {code}) — falling back to python -m venv...")
326
+ run_subprocess_safe(
327
+ [_sys.executable, "-m", "venv", VENV_DIR],
328
+ cwd="/tmp", raise_on_error=True,
329
+ )
330
+
331
  pytest_bin = os.path.join(VENV_DIR, "bin", "pytest")
332
  req_path = os.path.join(REPO_DIR, "requirements.txt")
333
  if os.path.exists(req_path):
334
  ui_log("Installing target repo deps via uv...")
335
  run_subprocess_safe(
336
  ["uv", "pip", "install", "--python", VENV_DIR, "--quiet", "-r", req_path],
337
+ cwd=REPO_DIR, timeout=600, raise_on_error=False,
338
  )
339
  return pytest_bin
340
 
language_runtime.py CHANGED
@@ -246,9 +246,36 @@ class PythonRuntime(LanguageRuntime):
246
  return any(os.path.exists(os.path.join(repo_dir, m)) for m in markers)
247
 
248
  def setup_env(self, repo_dir: str, persistent_dir: str = "/data") -> EnvConfig:
 
249
  venv_dir = os.path.join(persistent_dir, "target_venv")
 
 
 
 
 
250
  if not os.path.exists(venv_dir):
251
- self._run(["uv", "venv", venv_dir], cwd="/tmp", raise_on_error=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  req_path = os.path.join(repo_dir, "requirements.txt")
253
  if os.path.exists(req_path):
254
  self._run(
@@ -263,6 +290,7 @@ class PythonRuntime(LanguageRuntime):
263
  ["uv", "pip", "install", "--python", venv_dir, "--quiet", "-e", ".[dev,test]"],
264
  cwd=repo_dir, timeout=600,
265
  )
 
266
  pytest_bin = os.path.join(venv_dir, "bin", "pytest")
267
  return EnvConfig(
268
  language="python",
 
246
  return any(os.path.exists(os.path.join(repo_dir, m)) for m in markers)
247
 
248
  def setup_env(self, repo_dir: str, persistent_dir: str = "/data") -> EnvConfig:
249
+ import sys
250
  venv_dir = os.path.join(persistent_dir, "target_venv")
251
+
252
+ # FIX: guarantee the parent directory exists before uv/venv tries to write into it.
253
+ # In HuggingFace Spaces /data is a mounted volume that may not be pre-created.
254
+ os.makedirs(persistent_dir, exist_ok=True)
255
+
256
  if not os.path.exists(venv_dir):
257
+ # FIX: pass --python explicitly so uv never has to auto-resolve a Python
258
+ # interpreter. Without this flag, uv exits 2 when UV_PYTHON is absent or
259
+ # the managed-Python toolchain cache is cold (common in Space restarts).
260
+ out, code = self._run(
261
+ ["uv", "venv", "--python", sys.executable, venv_dir],
262
+ cwd="/tmp",
263
+ )
264
+ if code != 0:
265
+ # FIX: graceful fallback to stdlib venv — always available because we
266
+ # are already running inside the correct interpreter.
267
+ ui_msg = (
268
+ f"uv venv failed (exit {code}) — falling back to "
269
+ f"python -m venv. uv output: {out.strip()[:200]}"
270
+ )
271
+ import warnings
272
+ warnings.warn(ui_msg)
273
+ self._run(
274
+ [sys.executable, "-m", "venv", venv_dir],
275
+ cwd="/tmp",
276
+ raise_on_error=True,
277
+ )
278
+
279
  req_path = os.path.join(repo_dir, "requirements.txt")
280
  if os.path.exists(req_path):
281
  self._run(
 
290
  ["uv", "pip", "install", "--python", venv_dir, "--quiet", "-e", ".[dev,test]"],
291
  cwd=repo_dir, timeout=600,
292
  )
293
+
294
  pytest_bin = os.path.join(venv_dir, "bin", "pytest")
295
  return EnvConfig(
296
  language="python",