JAA-ATS-Tool / Dockerfile
saitejatirunagari's picture
fix: sanitize fontawesome OTF for Tectonic + dedupe keywords (no duplicates)
e2b8b62
Raw
History Blame
4.82 kB
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"]