# MLSysBook Quarto Build Container # Based on Ubuntu 22.04 with all dependencies pre-installed # This container eliminates the 30-45 minute setup time for Linux builds FROM ubuntu:22.04 # === PATH CONFIGURATION === # Paths are hardcoded since they're stable and ARG scope causes issues in multi-layer builds # Set environment variables ENV DEBIAN_FRONTEND=noninteractive ENV R_LIBS_USER=/usr/local/lib/R/library ENV QUARTO_LOG_LEVEL=INFO ENV PYTHONIOENCODING=utf-8 ENV LANG=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8 ENV PATH=/usr/local/texlive/bin/x86_64-linux:$PATH # === PHASE 0: COPY DEPENDENCY FILES EARLY (for better cache efficiency) === # Copy dependency files early for better cache efficiency COPY book/tools/dependencies/requirements.txt /tmp/requirements.txt COPY book/tools/dependencies/install_packages.R /tmp/install_packages.R COPY book/tools/dependencies/tl_packages /tmp/tl_packages COPY book/docker/linux/verify_r_packages.R /tmp/verify_r_packages.R # Note: Python packages are installed later in Phase 10 # Early cleanup RUN rm -rf /var/lib/apt/lists/* && \ apt-get clean # === PHASE 1: LOCALE CONFIGURATION === RUN echo "๐Ÿš€ === STARTING LOCALE CONFIGURATION ===" && \ echo "๐Ÿ” Checking system readiness..." && \ if [ -f /etc/os-release ]; then \ echo "โœ… OS release file found"; \ cat /etc/os-release | grep PRETTY_NAME; \ else \ echo "โŒ OS release file not found"; \ exit 1; \ fi && \ echo "๐Ÿ“ฆ Installing locales package..." && \ apt-get update && apt-get install -y locales && \ echo "๐Ÿ“ฆ Locales package installed" && \ echo "๐Ÿ”ง Generating en_US.UTF-8 locale..." && \ locale-gen en_US.UTF-8 && \ echo "๐Ÿ“„ Locale generated" && \ echo "๐Ÿ”ง Updating system locale..." && \ update-locale LANG=en_US.UTF-8 && \ echo "๐Ÿ”ง System locale updated" && \ echo "๐Ÿงน Cleaning package cache..." && \ rm -rf /var/lib/apt/lists/* && \ echo "โœ… Locale configuration complete" # === PHASE 2: SYSTEM DEPENDENCIES === RUN echo "๐Ÿš€ === STARTING SYSTEM DEPENDENCIES INSTALLATION ===" && \ echo "โฐ Estimated time: 2-3 minutes" && \ echo "๐Ÿ“Š Free disk space: $(df -h / | tail -1 | awk '{print $4}')" && \ start_time=$(date +%s) && \ \ echo "๐Ÿ”„ Updating package lists..." && \ apt-get update && \ \ echo "๐Ÿ“ฆ Installing core system packages (25 packages)..." && \ echo "๐Ÿ“‹ Package list:" && \ echo " - fonts-dejavu" && \ echo " - fonts-freefont-ttf" && \ echo " - gdk-pixbuf2.0-bin" && \ echo " - libcairo2" && \ echo " - libfontconfig1" && \ echo " - libfontconfig1-dev" && \ echo " - libfreetype6" && \ echo " - libfreetype6-dev" && \ echo " - libpango-1.0-0" && \ echo " - libpangocairo-1.0-0" && \ echo " - libpangoft2-1.0-0" && \ echo " - libxml2-dev" && \ echo " - libcurl4-openssl-dev" && \ echo " - libjpeg-dev" && \ echo " - libtiff5-dev" && \ echo " - libpng-dev" && \ echo " - libharfbuzz-dev" && \ echo " - libfribidi-dev" && \ echo " - librsvg2-dev" && \ echo " - libgdal-dev" && \ echo " - libudunits2-dev" && \ echo " - wget" && \ echo " - curl" && \ echo " - git" && \ apt-get install -y \ fonts-dejavu \ fonts-freefont-ttf \ gdk-pixbuf2.0-bin \ libcairo2 \ libfontconfig1 \ libfontconfig1-dev \ libfreetype6 \ libfreetype6-dev \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libpangoft2-1.0-0 \ libxml2-dev \ libcurl4-openssl-dev \ libjpeg-dev \ libtiff5-dev \ libpng-dev \ libharfbuzz-dev \ libfribidi-dev \ librsvg2-dev \ libgdal-dev \ libudunits2-dev \ wget \ curl \ git && \ echo "๐Ÿ“ฆ All system packages installed successfully" && \ \ echo "๐Ÿ” Verifying critical packages..." && \ if command -v wget >/dev/null 2>&1; then \ echo "๐Ÿ“ฆ wget available"; \ else \ echo "โŒ wget not found"; \ exit 1; \ fi && \ if command -v curl >/dev/null 2>&1; then \ echo "๐Ÿ“ฆ curl available"; \ else \ echo "โŒ curl not found"; \ exit 1; \ fi && \ if command -v git >/dev/null 2>&1; then \ echo "๐Ÿ“ฆ git available"; \ else \ echo "โŒ git not found"; \ exit 1; \ fi && \ \ echo "๐Ÿงน Cleaning package cache..." && \ rm -rf /var/lib/apt/lists/* && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === SYSTEM DEPENDENCIES COMPLETE === (${duration}s)" && \ echo "๐Ÿ“Š Free disk space: $(df -h / | tail -1 | awk '{print $4}')" # === PHASE 3: INKSCAPE INSTALLATION === RUN echo "๐Ÿš€ === STARTING INKSCAPE INSTALLATION ===" && \ echo "โฐ Estimated time: 1-2 minutes" && \ start_time=$(date +%s) && \ \ echo "๐Ÿ”„ Adding Inkscape PPA repository..." && \ apt-get update && \ echo "๐Ÿ“ฆ Installing software-properties-common..." && \ apt-get install -y software-properties-common && \ echo "๐Ÿ“ฆ software-properties-common installed" && \ echo "๐Ÿ”ง Adding Inkscape PPA..." && \ add-apt-repository ppa:inkscape.dev/stable -y && \ echo "๐Ÿ“ฆ Inkscape PPA added" && \ \ echo "๐Ÿ“ฆ Installing Inkscape..." && \ apt-get update && \ apt-get install -y inkscape && \ echo "๐Ÿ“ฆ Inkscape installed" && \ \ echo "๐Ÿงน Cleaning package cache..." && \ rm -rf /var/lib/apt/lists/* && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === INKSCAPE INSTALLATION COMPLETE === (${duration}s)" # Install font dependencies (note: fonts-freefont-ttf already installed above) RUN echo "๐Ÿš€ === STARTING FONT INSTALLATION ===" && \ echo "๐Ÿ“ฆ Installing additional fonts..." && \ apt-get update && apt-get install -y \ fonts-liberation \ fontconfig && \ echo "๐Ÿ“ฆ Fonts installed" && \ echo "๐Ÿ”ง Updating font cache..." && \ fc-cache -fv && \ echo "๐Ÿ“„ Font cache updated" && \ echo "๐Ÿงน Cleaning package cache..." && \ rm -rf /var/lib/apt/lists/* && \ echo "โœ… Font installation complete" # Test Inkscape SVG to PDF conversion (same as your workflow) RUN echo "๐Ÿš€ === STARTING INKSCAPE TEST ===" && \ echo "๐Ÿ“‹ Creating test SVG file..." && \ echo '' > test.svg && \ echo "๐Ÿ“„ Test SVG created" && \ echo "๐Ÿ”„ Converting SVG to PDF..." && \ inkscape --export-type=pdf --export-filename=test.pdf test.svg && \ echo "๐Ÿ“ฆ Conversion completed" && \ if [ -f test.pdf ]; then \ echo "โœ… Inkscape SVG to PDF conversion successful!"; \ echo "๐Ÿ“Š PDF file details:"; \ ls -lh test.pdf; \ else \ echo "โŒ Inkscape SVG to PDF conversion failed."; \ exit 1; \ fi && \ echo "๐Ÿงน Cleaning up test files..." && \ rm -f test.svg test.pdf && \ echo "โœ… Inkscape test complete" # === PHASE 4: GHOSTSCRIPT INSTALLATION === RUN echo "๐Ÿš€ === STARTING GHOSTSCRIPT INSTALLATION ===" && \ echo "โฐ Estimated time: 30 seconds" && \ start_time=$(date +%s) && \ \ echo "๐Ÿ“ฆ Installing Ghostscript..." && \ apt-get update && apt-get install -y ghostscript && \ echo "๐Ÿ“ฆ Ghostscript installed" && \ echo "๐Ÿ“Š Ghostscript version:" && \ gs --version && \ \ echo "๐Ÿงน Cleaning package cache..." && \ rm -rf /var/lib/apt/lists/* && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === GHOSTSCRIPT INSTALLATION COMPLETE === (${duration}s)" # === PHASE 5: TEX LIVE INSTALLATION === # Step 5.1: Install TeX Live prerequisites RUN echo "๐Ÿš€ === STARTING TEX LIVE INSTALLATION ===" && \ echo "โฐ Estimated time: 8-12 minutes (largest phase)" && \ echo "๐Ÿ“Š Free disk space before: $(df -h / | tail -1 | awk '{print $4}')" && \ \ echo "๐Ÿ“ฆ Installing TeX Live prerequisites..." && \ echo "๐Ÿ“‹ Prerequisites:" && \ echo " - perl" && \ echo " - wget" && \ echo " - xzdec" && \ apt-get update && apt-get install -y \ perl \ wget \ xzdec && \ echo "๐Ÿ“ฆ Prerequisites installed" && \ \ echo "๐Ÿ” Verifying prerequisites..." && \ if command -v perl >/dev/null 2>&1; then \ echo "๐Ÿ“ฆ perl available"; \ else \ echo "โŒ perl not found"; \ exit 1; \ fi && \ if command -v wget >/dev/null 2>&1; then \ echo "๐Ÿ“ฆ wget available"; \ else \ echo "โŒ wget not found"; \ exit 1; \ fi && \ if command -v xzdec >/dev/null 2>&1; then \ echo "๐Ÿ“ฆ xzdec available"; \ else \ echo "โŒ xzdec not found"; \ exit 1; \ fi && \ \ rm -rf /var/lib/apt/lists/* && \ echo "โœ… TeX Live prerequisites installed" # Step 5.2: Download and extract TeX Live installer with mirror fallbacks RUN echo "๐Ÿ”„ Downloading TeX Live installer (~4MB) with mirror fallbacks..." && \ SUCCESS=false && \ for mirror in \ "https://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz" \ "https://mirrors.mit.edu/CTAN/systems/texlive/tlnet/install-tl-unx.tar.gz" \ "https://ctan.math.washington.edu/tex-archive/systems/texlive/tlnet/install-tl-unx.tar.gz" \ "https://mirrors.rit.edu/CTAN/systems/texlive/tlnet/install-tl-unx.tar.gz" \ "https://mirror.las.iastate.edu/tex-archive/systems/texlive/tlnet/install-tl-unx.tar.gz"; do \ echo "๐Ÿ”„ Trying mirror: $mirror" && \ if wget --timeout=30 --tries=2 -O /tmp/install-tl-unx.tar.gz "$mirror"; then \ echo "โœ… Download successful from: $mirror" && \ SUCCESS=true && \ break; \ else \ echo "โŒ Failed to download from: $mirror" && \ rm -f /tmp/install-tl-unx.tar.gz; \ fi; \ done && \ if [ "$SUCCESS" = false ]; then \ echo "โŒ All mirrors failed, cannot proceed" && \ exit 1; \ fi && \ echo "๐Ÿ“ฅ Download completed" && \ echo "๐Ÿ“Š Downloaded file size:" && \ ls -lh /tmp/install-tl-unx.tar.gz && \ \ echo "๐Ÿ“ฆ Extracting TeX Live installer..." && \ cd /tmp && tar -xzf install-tl-unx.tar.gz && \ echo "๐Ÿ“ฆ Extraction completed" && \ echo "๐Ÿ“Š Extracted files:" && \ ls -la /tmp/install-tl-* && \ echo "โœ… TeX Live installer ready" # Step 5.3: Create TeX Live profile and install base system with retry logic RUN echo "๐Ÿ”ง Creating TeX Live installation profile..." && \ echo "selected_scheme scheme-medium" > /tmp/texlive.profile && \ echo "tlpdbopt_install_docfiles 0" >> /tmp/texlive.profile && \ echo "tlpdbopt_install_srcfiles 0" >> /tmp/texlive.profile && \ echo "TEXDIR /usr/local/texlive" >> /tmp/texlive.profile && \ echo "TEXMFCONFIG /usr/local/texlive/texmf-config" >> /tmp/texlive.profile && \ echo "TEXMFHOME /usr/local/texlive/texmf-home" >> /tmp/texlive.profile && \ echo "TEXMFLOCAL /usr/local/texlive/texmf-local" >> /tmp/texlive.profile && \ echo "TEXMFSYSCONFIG /usr/local/texlive/texmf-config" >> /tmp/texlive.profile && \ echo "TEXMFSYSVAR /usr/local/texlive/texmf-var" >> /tmp/texlive.profile && \ echo "TEXMFVAR /usr/local/texlive/texmf-var" >> /tmp/texlive.profile && \ echo "๐Ÿ“„ Profile created" && \ echo "๐Ÿ“Š Profile contents:" && \ cat /tmp/texlive.profile && \ \ echo "๐Ÿ”„ Installing TeX Live base system with retry logic..." && \ INSTALL_SUCCESS=false && \ for attempt in 1 2 3; do \ echo "๐Ÿ”„ Installation attempt $attempt/3..." && \ if /tmp/install-tl-*/install-tl --profile=/tmp/texlive.profile; then \ echo "โœ… TeX Live installation successful on attempt $attempt" && \ INSTALL_SUCCESS=true && \ break; \ else \ echo "โŒ Installation attempt $attempt failed" && \ if [ $attempt -lt 3 ]; then \ echo "โณ Waiting 10 seconds before retry..." && \ sleep 10; \ fi; \ fi; \ done && \ if [ "$INSTALL_SUCCESS" = false ]; then \ echo "โŒ All installation attempts failed" && \ echo "๐Ÿ“‹ Checking for partial installation..." && \ ls -la /usr/local/texlive/ || echo "No TeX Live directory found" && \ exit 1; \ fi && \ echo "๐Ÿ“ฆ TeX Live base system installed" # Step 5.4: Configure TeX Live PATH RUN echo "๐Ÿ”ง Setting up TeX Live PATH..." && \ echo 'export PATH=/usr/local/texlive/bin/x86_64-linux:$PATH' >> /etc/bash.bashrc && \ export PATH=/usr/local/texlive/bin/x86_64-linux:$PATH && \ echo "๐Ÿ”ง TeX Live PATH configured" && \ \ echo "๐Ÿ“ Checking if tlmgr is available..." && \ if command -v tlmgr >/dev/null 2>&1; then \ echo "๐Ÿ“ฆ tlmgr available"; \ tlmgr --version | head -1; \ else \ echo "โŒ tlmgr not found or not working"; \ exit 1; \ fi && \ echo "โœ… TeX Live PATH and tlmgr verified" # Step 5.5: Install TeX Live packages with improved error handling RUN echo "๐Ÿ“Š Analyzing tl_packages file..." && \ collection_count=$(grep -c '^collection-' /tmp/tl_packages) && \ echo "๐Ÿ“ฆ Found $collection_count TeX Live collections to install" && \ \ echo "๐Ÿ”„ Installing TeX Live collections with retry logic..." && \ export PATH=/usr/local/texlive/bin/x86_64-linux:$PATH && \ i=1 && \ failed_packages="" && \ while IFS= read -r collection; do \ case "$collection" in \ collection-*) \ echo "๐Ÿ“ฆ [$i/$collection_count] Installing $collection..."; \ if command -v tlmgr >/dev/null 2>&1; then \ success=false; \ for retry in 1 2; do \ if tlmgr install "$collection"; then \ echo "โœ… Successfully installed $collection"; \ success=true; \ break; \ else \ echo "โŒ Attempt $retry failed for $collection"; \ if [ $retry -lt 2 ]; then \ echo "โณ Retrying in 5 seconds..."; \ sleep 5; \ fi; \ fi; \ done; \ if [ "$success" = false ]; then \ echo "โš ๏ธ Failed to install $collection after retries, continuing..."; \ failed_packages="$failed_packages $collection"; \ fi; \ else \ echo "โš ๏ธ tlmgr not available, skipping $collection"; \ fi; \ i=$((i+1)); \ ;; \ esac; \ done < /tmp/tl_packages && \ if [ -n "$failed_packages" ]; then \ echo "โš ๏ธ Some packages failed to install:$failed_packages"; \ echo "๐Ÿ“‹ This may not be critical for basic functionality"; \ fi && \ echo "โœ… TeX Live packages installation completed" # Step 5.6: TeX Live cleanup and completion RUN echo "๐Ÿงน Cleaning up TeX Live installer..." && \ rm -rf /tmp/install-tl-* /tmp/texlive.profile /tmp/install-tl-unx.tar.gz && \ echo "โœ… === TEX LIVE INSTALLATION COMPLETE ===" && \ echo "๐Ÿ“Š Free disk space after: $(df -h / | tail -1 | awk '{print $4}')" && \ echo "๐Ÿ“Š TeX Live disk usage: $(du -sh /usr/local/texlive 2>/dev/null || echo 'N/A')" # Verify TeX Live installation (with error handling) RUN echo "๐Ÿ”„ Verifying TeX Live installation..." && \ export PATH=/usr/local/texlive/bin/x86_64-linux:$PATH && \ echo "๐Ÿ“ PATH: $PATH" && \ echo "๐Ÿ“ Checking TeX Live directory:" && \ ls -la /usr/local/texlive/ || echo "โŒ TeX Live directory not found" && \ ls -la /usr/local/texlive/bin/ || echo "โŒ TeX Live bin directory not found" && \ if [ -f /usr/local/texlive/bin/x86_64-linux/lualatex ]; then \ echo "โœ… lualatex found"; \ /usr/local/texlive/bin/x86_64-linux/lualatex --version | head -1; \ else \ echo "โŒ lualatex not found, checking for alternative locations"; \ find /usr/local/texlive -name "lualatex" -type f 2>/dev/null || echo "No lualatex found anywhere"; \ fi && \ echo "โœ… TeX Live verification complete (allowing partial failures)" # === PHASE 6: R INSTALLATION === RUN echo "๐Ÿš€ === STARTING R INSTALLATION ===" && \ echo "โฐ Estimated time: 1-2 minutes" && \ start_time=$(date +%s) && \ \ echo "๐Ÿ“ฆ Installing R and development packages..." && \ echo "๐Ÿ“‹ R packages:" && \ echo " - r-base" && \ echo " - r-base-dev" && \ echo " - r-recommended" && \ apt-get update && apt-get install -y \ r-base \ r-base-dev \ r-recommended && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to install R packages"; \ exit 1; \ fi && \ echo "๐Ÿ“ฆ R packages installed" && \ \ echo "๐Ÿงน Cleaning package cache..." && \ rm -rf /var/lib/apt/lists/* && \ \ echo "๐Ÿ“Š R version: $(R --version | head -1)" && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to verify R installation"; \ exit 1; \ fi && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === R INSTALLATION COMPLETE === (${duration}s)" # === PHASE 7: R PACKAGE INSTALLATION === RUN echo "๐Ÿš€ === STARTING R PACKAGE INSTALLATION ===" && \ echo "โฐ Estimated time: 2-3 minutes" && \ start_time=$(date +%s) && \ \ echo "Running R setup script with the following files:" && \ echo " - /tmp/install_packages.R (from book/tools/dependencies/install_packages.R)" && \ echo " - /tmp/verify_r_packages.R (from book/docker/linux/verify_r_packages.R)" && \ \ echo "๐Ÿ“ฆ Installing R packages..." && \ Rscript /tmp/install_packages.R && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to install R packages"; \ exit 1; \ fi && \ echo "โœ… R packages installed successfully" && \ \ echo "๐Ÿ” Verifying R packages..." && \ Rscript /tmp/verify_r_packages.R && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to verify R packages"; \ exit 1; \ fi && \ echo "โœ… R packages verified successfully" && \ \ echo "๐Ÿงน Cleaning up R installation files..." && \ rm -f /tmp/install_packages.R /tmp/verify_r_packages.R && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === R PACKAGE INSTALLATION COMPLETE === (${duration}s)" # === PHASE 8: PYTHON INSTALLATION === RUN echo "๐Ÿš€ === STARTING PYTHON INSTALLATION ===" && \ echo "โฐ Estimated time: 1 minute" && \ start_time=$(date +%s) && \ \ echo "๐Ÿ“ฆ Installing Python 3 and development packages..." && \ echo "๐Ÿ“‹ Python packages:" && \ echo " - python3" && \ echo " - python3-pip" && \ echo " - python3-dev" && \ apt-get update && apt-get install -y \ python3 \ python3-pip \ python3-dev && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to install Python packages"; \ exit 1; \ fi && \ echo "๐Ÿ“ฆ Python packages installed" && \ \ echo "๐Ÿงน Cleaning package cache..." && \ rm -rf /var/lib/apt/lists/* && \ \ echo "๐Ÿ“Š Python version: $(python3 --version)" && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to verify Python installation"; \ exit 1; \ fi && \ echo "๐Ÿ“Š Pip version: $(pip3 --version)" && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to verify pip installation"; \ exit 1; \ fi && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === PYTHON INSTALLATION COMPLETE === (${duration}s)" # === PHASE 9: QUARTO INSTALLATION === RUN echo "๐Ÿš€ === STARTING QUARTO INSTALLATION ===" && \ echo "โฐ Estimated time: 1 minute" && \ start_time=$(date +%s) && \ \ echo "๐Ÿ“ฆ Downloading Quarto 1.7.31..." && \ wget -q https://github.com/quarto-dev/quarto-cli/releases/download/v1.7.31/quarto-1.7.31-linux-amd64.deb && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to download Quarto"; \ exit 1; \ fi && \ echo "๐Ÿ“ฅ Download completed" && \ echo "๐Ÿ“Š Downloaded file size:" && \ ls -lh quarto-1.7.31-linux-amd64.deb && \ \ echo "๐Ÿ“ฆ Installing Quarto..." && \ dpkg -i quarto-1.7.31-linux-amd64.deb && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to install Quarto"; \ exit 1; \ fi && \ echo "๐Ÿ“ฆ Quarto installed" && \ \ echo "๐Ÿงน Cleaning up installer..." && \ rm quarto-1.7.31-linux-amd64.deb && \ echo "๐Ÿงน Installer cleaned up" && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === QUARTO INSTALLATION COMPLETE === (${duration}s)" # Create R library directory RUN echo "๐Ÿš€ === STARTING R LIBRARY SETUP ===" && \ echo "๐Ÿ“ Creating R library directory..." && \ mkdir -p $R_LIBS_USER && \ echo "โœ… R library directory created: $R_LIBS_USER" && \ echo "โœ… R library setup complete" # === PHASE 10: PYTHON PACKAGES === RUN echo "๐Ÿš€ === STARTING PYTHON PACKAGE INSTALLATION ===" && \ echo "โฐ Estimated time: 1-2 minutes" && \ start_time=$(date +%s) && \ \ echo "๐Ÿ”„ Upgrading pip..." && \ pip3 install --upgrade pip && \ \ echo "๐Ÿ“Š Analyzing requirements.txt..." && \ package_count=$(grep -v '^#' /tmp/requirements.txt | grep -v '^$' | wc -l) && \ echo "๐Ÿ“ฆ Found $package_count Python packages to install" && \ \ echo "๐Ÿ”„ Installing Python packages with space optimization..." && \ pip3 install --no-cache-dir -r /tmp/requirements.txt && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to install Python packages"; \ exit 1; \ fi && \ \ echo "๐Ÿงน Cleaning Python installation caches..." && \ pip3 cache purge && \ find /usr -name "*.pyc" -delete && \ find /usr -name "__pycache__" -type d -exec rm -rf {} + || true && \ \ echo "๐Ÿ“Š Installed Python packages:" && \ pip3 list | head -10 && \ echo "๐Ÿ“Š Total packages: $(pip3 list | wc -l)" && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === PYTHON PACKAGES COMPLETE === (${duration}s)" # === PHASE 9: R PACKAGES === # Step 9.1: Set up R environment and install remotes RUN echo "๐Ÿš€ === STEP 9.1: SETTING UP R ENVIRONMENT ===" && \ R --slave -e " \ options(repos = c(CRAN = 'https://cran.rstudio.com')); \ cat('๐Ÿ”„ Setting up R environment...\n'); \ cat(paste('R library path:', Sys.getenv('R_LIBS_USER'), '\n')); \ lib_path <- Sys.getenv('R_LIBS_USER'); \ dir.create(lib_path, showWarnings = FALSE, recursive = TRUE); \ .libPaths(lib_path); \ cat('๐Ÿ“ฆ Installing remotes package...\n'); \ install.packages('remotes'); \ cat('โœ… R environment setup complete\n'); \ " && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to set up R environment"; \ exit 1; \ fi && \ echo "โœ… R environment setup successful" # Step 9.2: Install R packages from install_packages.R or fallback RUN echo "๐Ÿš€ === STEP 9.2: INSTALLING R PACKAGES ===" && \ R --slave -e " \ options(repos = c(CRAN = 'https://cran.rstudio.com')); \ lib_path <- Sys.getenv('R_LIBS_USER'); \ .libPaths(lib_path); \ if (file.exists('/tmp/install_packages.R')) { \ cat('๐Ÿ“ฆ Installing packages from tools/dependencies/install_packages.R...\n'); \ source('/tmp/install_packages.R'); \ } else { \ cat('โš ๏ธ No tools/dependencies/install_packages.R found, installing common packages\n'); \ pkgs <- c('rmarkdown', 'knitr', 'tidyverse', 'ggplot2', 'bookdown'); \ cat(paste('๐Ÿ“ฆ Installing packages:', paste(pkgs, collapse=', '), '\n')); \ install.packages(pkgs); \ }; \ cat('โœ… R package installation complete\n'); \ " && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to install R packages"; \ exit 1; \ fi && \ echo "โœ… R packages installed successfully" # Step 9.3: Verify R package installation RUN echo "๐Ÿš€ === STEP 9.3: VERIFYING R PACKAGES ===" && \ R --slave -e " \ lib_path <- Sys.getenv('R_LIBS_USER'); \ .libPaths(lib_path); \ cat('๐Ÿ“Š Installed packages:\n'); \ ip <- installed.packages()[, 'Package']; \ print(head(ip, 10)); \ cat(paste('Total packages installed:', nrow(ip), '\n')); \ " && \ if [ $? -ne 0 ]; then \ echo "โŒ Failed to verify R packages"; \ exit 1; \ fi && \ echo "โœ… R package verification successful" # === PHASE 11: R PACKAGE VERIFICATION === # Note: R package verification is now done immediately after R package installation (Phase 6.5) # === PHASE 12: COMPREHENSIVE CLEANUP === RUN echo "๐Ÿš€ === STARTING COMPREHENSIVE CLEANUP ===" && \ echo "๐Ÿ“Š Disk space before cleanup: $(df -h / | tail -1 | awk '{print $4}')" && \ start_time=$(date +%s) && \ \ echo "๐Ÿงน Removing temporary files..." && \ rm -rf /tmp/* && \ rm -rf /var/tmp/* && \ \ echo "๐Ÿงน Cleaning package caches..." && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ rm -rf /var/cache/apt/* && \ \ echo "๐Ÿงน Cleaning Python caches..." && \ find /usr -name "*.pyc" -delete && \ find /usr -name "__pycache__" -type d -exec rm -rf {} + || true && \ pip3 cache purge || true && \ \ echo "๐Ÿงน Cleaning R temporary files..." && \ rm -rf /tmp/Rtmp* || true && \ rm -rf /var/lib/R/site-library/*/help || true && \ \ echo "๐Ÿงน Cleaning TeX Live caches and docs..." && \ rm -rf /usr/local/texlive/*/texmf-var/luatex-cache/* || true && \ rm -rf /usr/local/texlive/*/texmf-var/web2c/* || true && \ \ echo "๐Ÿงน Removing unnecessary system files..." && \ rm -rf /usr/share/doc/* && \ rm -rf /usr/share/man/* && \ rm -rf /usr/share/info/* && \ rm -rf /var/log/* && \ \ end_time=$(date +%s) && \ duration=$((end_time - start_time)) && \ echo "โœ… === COMPREHENSIVE CLEANUP COMPLETE === (${duration}s)" && \ echo "๐Ÿ“Š Final disk space: $(df -h / | tail -1 | awk '{print $4}')" # Set working directory WORKDIR /workspace # === PHASE 13: FINAL VERIFICATION === RUN echo "๐Ÿš€ === STARTING FINAL VERIFICATION ===" && \ export PATH=/usr/local/texlive/bin/x86_64-linux:$PATH && \ echo "๐Ÿ“Š Checking Quarto..." && \ if command -v quarto >/dev/null 2>&1; then \ quarto --version && \ echo "โœ… Quarto verified"; \ else \ echo "โŒ Quarto not found in PATH"; \ echo "๐Ÿ“ Checking for quarto in common locations:"; \ find /usr -name "quarto" -type f 2>/dev/null || echo "No quarto found"; \ exit 1; \ fi && \ echo "๐Ÿ“Š Checking Python..." && \ if command -v python3 >/dev/null 2>&1; then \ python3 --version && \ echo "โœ… Python verified"; \ else \ echo "โŒ Python3 not found"; \ exit 1; \ fi && \ echo "๐Ÿ“Š Checking R..." && \ if command -v R >/dev/null 2>&1; then \ R --version && \ echo "โœ… R verified"; \ else \ echo "โŒ R not found"; \ exit 1; \ fi && \ echo "๐Ÿ“Š Checking LaTeX..." && \ if command -v lualatex >/dev/null 2>&1; then \ lualatex --version && \ echo "โœ… LaTeX verified"; \ else \ echo "โŒ lualatex not found"; \ echo "๐Ÿ“ Checking for lualatex in TeX Live:"; \ find /usr/local/texlive -name "lualatex" -type f 2>/dev/null || echo "No lualatex found"; \ exit 1; \ fi && \ echo "โœ… Final verification complete" # Health check RUN export PATH=/usr/local/texlive/bin/x86_64-linux:$PATH && \ echo "โœ… Container build completed successfully" && \ echo "๐Ÿ“Š Quarto version: $(quarto --version)" && \ echo "๐Ÿ“Š Python version: $(python3 --version)" && \ echo "๐Ÿ“Š R version: $(R --version | head -1)" && \ echo "๐Ÿ“Š TeX Live: $(lualatex --version | head -1)"