Spaces:
Sleeping
Sleeping
| FROM python:3.11-slim | |
| # System deps for Playwright + Node.js + git | |
| RUN apt-get update && apt-get install -y \ | |
| wget curl gnupg ca-certificates git \ | |
| libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \ | |
| libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \ | |
| libgbm1 libasound2 libxshmfence1 libx11-xcb1 \ | |
| libxcb-dri3-0 libpango-1.0-0 libcairo2 libfontconfig1 \ | |
| fonts-liberation libappindicator3-1 xdg-utils \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # Install Node.js 20 LTS (needed to run ever-jobs NestJS API) | |
| RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ | |
| && apt-get install -y nodejs \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # Create non-root user (HF Spaces requirement) | |
| RUN useradd -m -u 1000 user | |
| USER user | |
| ENV HOME=/home/user PATH=/home/user/.local/bin:$PATH | |
| WORKDIR /app | |
| # Install Python deps (separate layer β cached unless requirements.txt changes) | |
| COPY --chown=user requirements.txt . | |
| RUN pip install --no-cache-dir --upgrade pip \ | |
| && pip install --no-cache-dir -r requirements.txt | |
| # Install Playwright Chromium browser | |
| RUN playwright install chromium | |
| # Install Scrapling's stealth browser (Camoufox) + fetcher deps for the | |
| # anti-block fetch layer. Non-fatal: if the Camoufox download hiccups, the | |
| # lightweight HTTP TLS-impersonation path (Fetcher) and the requests fallback | |
| # still work β only the Cloudflare-bypass StealthyFetcher would be unavailable. | |
| RUN (scrapling install || python -m scrapling install || true) | |
| # Install Tectonic (self-contained LaTeX engine) for compiling user resume | |
| # LaTeX β PDF. Pin a specific MUSL-STATIC release: the rolling drop-sh installer | |
| # shipped a glibc build that crashed at COMPILE time with "free(): invalid | |
| # pointer" (a glibc heap abort β note it ran `tectonic --version` fine, so a | |
| # version gate did not catch it). The musl static binary does not link glibc, so | |
| # it cannot hit that class of crash and is maximally portable in the container. | |
| ENV TECTONIC_CACHE_DIR=/home/user/.cache/tectonic | |
| RUN mkdir -p /home/user/.local/bin /home/user/.cache/tectonic \ | |
| && cd /tmp \ | |
| && curl --proto '=https' --tlsv1.2 -fsSL -o tectonic.tar.gz \ | |
| https://github.com/tectonic-typesetting/tectonic/releases/download/tectonic%400.16.9/tectonic-0.16.9-x86_64-unknown-linux-musl.tar.gz \ | |
| && tar -xzf tectonic.tar.gz \ | |
| && mv tectonic /home/user/.local/bin/tectonic \ | |
| && chmod +x /home/user/.local/bin/tectonic \ | |
| && rm -f tectonic.tar.gz \ | |
| && tectonic --version | |
| # Pre-warm the bundle cache by compiling the SANITIZED resume β the exact source | |
| # the server compiles at runtime (_sanitize_for_tectonic strips fontawesome5's OTF | |
| # loader, which SEGFAULTS Tectonic's XeTeX, and the FiraMono font loader). This | |
| # caches every package/font the real compile uses AND proves the sanitized template | |
| # actually compiles at BUILD time (grep the build log for "TECTONIC_REAL_WARMUP"). | |
| # Build gate is intentionally non-fatal so a transient warmup hiccup never bricks | |
| # the Space; the OK/FAILED marker + log tail make the outcome visible. | |
| COPY --chown=user assets/default_resume.tectonic.tex /tmp/warm-resume.tex | |
| RUN (tectonic --outdir /tmp /tmp/warm-resume.tex \ | |
| && echo "TECTONIC_REAL_WARMUP_OK" \ | |
| || (echo "TECTONIC_REAL_WARMUP_FAILED β full log follows:" \ | |
| && cat /tmp/warm-resume.log 2>/dev/null | tail -80 || true)) \ | |
| && rm -f /tmp/warm-resume.pdf | |
| # Hard build gate: fail the image build if the Tectonic binary is missing/broken | |
| # (so we never silently ship a no-LaTeX-engine image β R16). | |
| RUN tectonic --version | |
| # Clone ever-jobs and install Node deps (separate layer β cached unless repo changes) | |
| # vendor/ is in .dockerignore so it never comes from the build context | |
| # nest-cli.json forces webpack which isn't installed; ts-node (nest start) is used at runtime instead | |
| RUN git clone https://github.com/ever-jobs/ever-jobs.git vendor/ever-jobs --depth=1 \ | |
| && cd vendor/ever-jobs \ | |
| && npm install --legacy-peer-deps \ | |
| && sed -i 's/"webpack": true/"webpack": false/g' nest-cli.json | |
| # Copy app code (this layer re-runs on every code change, which is expected) | |
| COPY --chown=user . . | |
| # Create required runtime directories | |
| RUN mkdir -p data/resume data/output/resumes data/output/reports \ | |
| data/research_cache data/logs data/output/run_history | |
| # Make startup script executable | |
| RUN chmod +x start.sh | |
| # Expose Streamlit port | |
| EXPOSE 7860 | |
| # Only keep non-conflicting Streamlit env vars. | |
| # STREAMLIT_SERVER_PORT and STREAMLIT_SERVER_ADDRESS are intentionally omitted: | |
| # they would conflict with the proxy architecture where api_server.py runs | |
| # uvicorn on 7860 and Streamlit is started on 127.0.0.1:8501 via CLI args. | |
| ENV STREAMLIT_SERVER_HEADLESS=true \ | |
| STREAMLIT_BROWSER_GATHER_USAGE_STATS=false | |
| CMD ["./start.sh"] | |