Files
peikarband/docker/Dockerfile
Ehsan.Asadi 963b6cecda
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
added cmd
2025-12-31 10:55:39 +03:30

213 lines
7.9 KiB
Docker

# Dockerfile - Peikarband Landing Application
# Optimized multi-stage build using base image
# Build arguments
ARG BASE_IMAGE=hub.peikarband.ir/peikarband/landing:base
ARG VERSION=latest
ARG BUILD_DATE
# ============================================
# Stage 1: Builder (using base image)
# ============================================
FROM ${BASE_IMAGE} AS builder
LABEL stage=builder
LABEL maintainer="Peikarband DevOps <devops@peikarband.ir>"
WORKDIR /build
# Base image already has:
# - Python 3.11
# - Node.js 20
# - bun
# - gcc, g++, make, curl, ca-certificates
# Verify tools are available
RUN echo "=== Build Environment ===" && \
python --version && \
node --version && \
npm --version && \
bun --version && \
echo "========================"
# ============================================
# Python Dependencies
# ============================================
# Copy Python requirements first (for layer caching)
COPY peikarband/requirements.txt .
# Install Python dependencies
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# ============================================
# Frontend Build (Reflex)
# ============================================
# Copy source code to /build/peikarband/ to preserve package structure
# This ensures peikarband.peikarband module can be found by Reflex
COPY peikarband/ /build/peikarband/
# Set PYTHONPATH to include /build (for peikarband package) and /build/peikarband (for src imports)
# This allows both peikarband.peikarband and src.* imports to work
ENV PYTHONPATH=/build:/build/peikarband
# Verify that peikarband.peikarband can be imported before running reflex
# This helps catch import errors early
# RUN cd /build && \
# python3 -c "from peikarband.peikarband import app; print('✅ peikarband.peikarband.app imported successfully')" && \
# echo "Import test passed"
# Initialize Reflex and build frontend from peikarband directory
# Reflex needs to run from the directory containing rxconfig.py
RUN cd /build/peikarband && \
reflex init --loglevel debug || true && \
reflex export --frontend-only --no-zip --loglevel debug && \
echo "Frontend export completed" && \
if [ -d .web/node_modules/.bin ]; then \
find .web/node_modules/.bin -type f -exec chmod +x {} \; && \
find .web/node_modules/.bin -type l | while read symlink; do \
target=$(readlink -f "$symlink" 2>/dev/null || true); \
if [ -n "$target" ] && [ -f "$target" ]; then \
chmod +x "$target" 2>/dev/null || true; \
fi; \
chmod +x "$symlink" 2>/dev/null || true; \
done && \
echo "✅ Set executable permissions for all .bin files (files and symlinks) and their targets"; \
fi
# Note: reflex export already builds and installs everything needed
# No additional npm install is required
RUN echo "Frontend built by reflex export"
# ============================================
# Stage 2: Runtime (using base image for Node.js)
# ============================================
FROM ${BASE_IMAGE} AS runtime
# Re-declare build arguments for this stage
ARG BASE_IMAGE=hub.peikarband.ir/peikarband/landing:base
ARG VERSION=latest
ARG BUILD_DATE
ARG GIT_COMMIT
ARG GIT_BRANCH
ARG BUILD_NUMBER
LABEL org.opencontainers.image.title="Peikarband Landing"
LABEL org.opencontainers.image.description="Peikarband hosting platform landing page"
LABEL org.opencontainers.image.vendor="Peikarband"
LABEL org.opencontainers.image.version="${VERSION}"
LABEL org.opencontainers.image.created="${BUILD_DATE}"
# Create non-root user
RUN groupadd -r peikarband && \
useradd -r -g peikarband -u 1000 -m -s /bin/bash peikarband
WORKDIR /app
# Note: We keep WORKDIR=/app (not /app/peikarband) to avoid Python importing
# /app/peikarband/peikarband/ as the peikarband package incorrectly
# The entrypoint script will cd to /app/peikarband before running reflex
# Base image already has everything we need:
# - Python 3.11
# - Node.js 20
# - curl, ca-certificates
# - tini (for proper init)
# No additional packages needed!
# Copy Python dependencies from builder
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
# Copy application code to /app/peikarband/ to create peikarband.peikarband structure
# With app_name="peikarband", Reflex expects to find peikarband.peikarband module
COPY --from=builder --chown=peikarband:peikarband /build/peikarband /app/peikarband
# Copy entrypoint script
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Create necessary directories
RUN mkdir -p /app/data /app/logs /app/uploaded_files
# Set proper permissions for application files
# Explicitly set executable permissions for node_modules/.bin files (both files and symlinks)
# Also fix permissions for symlink targets
RUN if [ -d /app/peikarband/.web/node_modules/.bin ]; then \
find /app/peikarband/.web/node_modules/.bin -type f -exec chmod +x {} \; && \
find /app/peikarband/.web/node_modules/.bin -type l | while read symlink; do \
target=$(readlink -f "$symlink" 2>/dev/null || true); \
if [ -n "$target" ] && [ -f "$target" ]; then \
chmod +x "$target" 2>/dev/null || true; \
fi; \
chmod +x "$symlink" 2>/dev/null || true; \
done && \
ls -la /app/peikarband/.web/node_modules/.bin/ | head -20 && \
echo "✅ Verified executable permissions for .bin files and symlink targets"; \
fi && \
chmod -R 777 /app/data /app/logs /app/uploaded_files
# Environment variables
# PYTHONPATH includes both /app and /app/peikarband
# - /app: allows importing peikarband from /app/peikarband/
# - /app/peikarband: allows importing src from /app/peikarband/src/
# This makes both peikarband.peikarband and src.* imports work correctly
# REFLEX_DIR points to the directory containing rxconfig.py
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=/app:/app/peikarband \
PATH="/app/.venv/bin:$PATH" \
REFLEX_DIR=/app/peikarband \
NODE_ENV=production
# Diagnostic information (before switching to non-root user)
RUN echo "=== Diagnostic Info ===" && \
if [ -f /app/peikarband/.web/node_modules/.bin/react-router ]; then \
ls -la /app/peikarband/.web/node_modules/.bin/react-router && \
file /app/peikarband/.web/node_modules/.bin/react-router || true; \
fi && \
if [ -f /app/peikarband/.web/node_modules/@react-router/dev/bin.js ]; then \
head -5 /app/peikarband/.web/node_modules/@react-router/dev/bin.js || true; \
fi && \
echo "======================="
# Health check (using backend health endpoint on port 8000)
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/ping || exit 1
# Switch to non-root user
USER peikarband
# Expose port
EXPOSE 3000 8000
# Use tini as init system
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
# Start application
# entrypoint.sh will cd to /app/peikarband and run reflex
# PYTHONPATH=/app:/app/peikarband allows Python to find both peikarband and peikarband.peikarband
# CMD ["run", "--env", "prod", "--loglevel", "info", "--frontend-port", "3000", "--backend-port", "8000"]
# ============================================
# Build Information
# ============================================
# ARG declarations are already done above in runtime stage
LABEL git.commit="${GIT_COMMIT}"
LABEL git.branch="${GIT_BRANCH}"
LABEL build.number="${BUILD_NUMBER}"
LABEL build.date="${BUILD_DATE}"
# ============================================
# Multi-Architecture Support
# ============================================
# This Dockerfile supports:
# - linux/amd64
# - linux/arm64 (with appropriate base image)
#
# Build with:
# docker buildx build --platform linux/amd64,linux/arm64 .