# CICD Base Setup - System dependencies and language runtimes only ARG PLAYWRIGHT_BROWSERS_IMAGE=mcr.microsoft.com/playwright:v1.56.1-jammy FROM ${PLAYWRIGHT_BROWSERS_IMAGE} AS playwright-browsers FROM ubuntu:22.04 # Build args for cache busting (base dependencies change rarely) ARG BASE_IMAGE_VERSION=v1.0.0 ARG BASE_IMAGE_HASH=unknown ENV BASE_IMAGE_VERSION=${BASE_IMAGE_VERSION} LABEL darkhelm.cicd-base.hash=${BASE_IMAGE_HASH} LABEL org.opencontainers.image.version=${BASE_IMAGE_VERSION} # Set timezone and make installs non-interactive ENV DEBIAN_FRONTEND=noninteractive ENV TZ=America/New_York # Configure timezone RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # Install apt-fast with proper GPG handling RUN apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ for i in 1 2 3; do \ echo "Attempt $i: Updating package lists..." && \ apt-get update && break || \ (echo "Update attempt $i failed, retrying..." && sleep 10); \ done && \ apt-get install -y \ software-properties-common \ gnupg \ ca-certificates \ curl \ wget \ && for i in 1 2 3; do \ echo "Attempt $i: Adding apt-fast PPA..." && \ add-apt-repository -y ppa:apt-fast/stable && \ apt-get update && \ apt-get install -y apt-fast && \ break || \ (echo "apt-fast installation attempt $i failed, retrying..." && sleep 10); \ done \ && rm -rf /var/lib/apt/lists/* # Configure apt-fast to use apt (not apt-get) with optimized settings RUN echo 'apt-fast apt-fast/maxdownloads string 10' | debconf-set-selections && \ echo 'apt-fast apt-fast/dlflag boolean true' | debconf-set-selections && \ echo 'apt-fast apt-fast/aptmanager string apt' | debconf-set-selections # Configure apt timeouts and retries RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \ echo 'Acquire::http::Timeout "60";' >> /etc/apt/apt.conf.d/80retries && \ echo 'Acquire::https::Timeout "60";' >> /etc/apt/apt.conf.d/80retries && \ echo 'Acquire::ftp::Timeout "60";' >> /etc/apt/apt.conf.d/80retries # Install system dependencies using apt-fast RUN apt-fast update && apt-fast install -y \ git \ curl \ ca-certificates \ software-properties-common \ build-essential \ openssh-client \ # Playwright Chromium runtime dependencies (Linux) libnspr4 \ libnss3 \ libatk1.0-0 \ libatspi2.0-0 \ libxcomposite1 \ libxdamage1 \ libxfixes3 \ libxrandr2 \ libgbm1 \ libxkbcommon0 \ libasound2 \ tzdata \ && rm -rf /var/lib/apt/lists/* # Install Python 3.14 with retry and fallback mechanisms RUN for i in 1 2 3; do \ echo "Attempt $i: Adding deadsnakes PPA..." && \ add-apt-repository -y ppa:deadsnakes/ppa && \ apt-get update && \ break || \ (echo "Attempt $i failed, retrying in 10s..." && sleep 10); \ done RUN for i in 1 2 3; do \ echo "Attempt $i: Installing Python 3.14..." && \ timeout 300 apt-fast install -y \ python3.14 \ python3.14-venv \ python3.14-dev && \ break || \ (echo "Attempt $i failed, retrying in 15s..." && sleep 15); \ done && \ rm -rf /var/lib/apt/lists/* # Install Node.js 24 with retry mechanism RUN for i in 1 2 3; do \ echo "Attempt $i: Installing Node.js 24..." && \ curl -fsSL --connect-timeout 30 --max-time 300 \ https://deb.nodesource.com/setup_24.x | bash - && \ apt-fast update && \ timeout 300 apt-fast install -y nodejs && \ break || \ (echo "Attempt $i failed, retrying in 15s..." && sleep 15); \ done && \ rm -rf /var/lib/apt/lists/* # Enable corepack for yarn and set up Yarn Berry RUN corepack enable \ && corepack prepare yarn@stable --activate \ && yarn set version berry # Configure Yarn globally for CI performance RUN yarn config set httpTimeout 60000 && \ yarn config set enableGlobalCache false && \ yarn config set compressionLevel 0 && \ yarn config set nmMode hardlinks-local # Install uv package manager globally without requiring GHCR pulls RUN set -eu && \ curl -LsSf https://astral.sh/uv/install.sh -o /tmp/uv-install.sh && \ sh /tmp/uv-install.sh && \ rm -f /tmp/uv-install.sh && \ cp /root/.local/bin/uv /usr/local/bin/uv && \ chmod +x /usr/local/bin/uv && \ uv --version # Install common development tools globally using npm (more reliable for global installs) RUN echo "=== Installing Global Development Tools ===" && \ # Use npm for global installations (works better than yarn global in Berry) npm install -g \ @playwright/test@1.56.1 \ typescript@5.3.3 \ eslint@9.33.0 \ prettier@3.6.2 \ vite@7.1.10 \ @types/node@20.16.0 && \ # Verify global tools are available which playwright && \ which tsc && \ which eslint && \ which prettier && \ echo "✓ Global Node.js development tools installed via npm" # Keep browser binaries in a deterministic location so E2E runs do not re-download them. ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright # Copy preinstalled browsers from the official Playwright image to avoid flaky CDN downloads. COPY --from=playwright-browsers /ms-playwright /ms-playwright RUN echo "=== Verifying preinstalled Playwright Chromium Browser ===" && \ chromium_count=$(find "${PLAYWRIGHT_BROWSERS_PATH}" -maxdepth 1 -type d -name 'chromium-*' | wc -l) && \ echo "playwright_chromium_dir_count=${chromium_count} path=${PLAYWRIGHT_BROWSERS_PATH}" && \ if [ "${chromium_count}" -eq 0 ]; then \ echo "❌ No Chromium browser directories found in preinstalled Playwright cache"; \ exit 1; \ fi && \ ls -la "${PLAYWRIGHT_BROWSERS_PATH}" && \ echo "✓ Playwright Chromium preinstalled at ${PLAYWRIGHT_BROWSERS_PATH}" # Pre-install common Python development dependencies globally # These are stable tools that rarely change and take time to compile RUN echo "=== Pre-installing Common Python Dev Tools ===" && \ # Create a global uv environment with common dev tools uv venv /opt/python-dev-tools && \ # Use system uv to install packages into the virtual environment uv pip install --python /opt/python-dev-tools/bin/python \ ruff>=0.6.0 \ pyright>=1.1.380 \ pytest>=7.4.0 \ pytest-asyncio>=0.21.0 \ pytest-cov>=4.1.0 \ pytest-mock>=3.12.0 \ pre-commit>=3.0.0 \ pyyaml>=6.0 \ yamllint>=1.35.0 \ toml-sort>=0.23.0 \ xdoctest>=1.1.0 \ language-formatters-pre-commit-hooks>=2.14.0 \ poethepoet>=0.24.0 && \ echo "✓ Common Python dev tools pre-installed in /opt/python-dev-tools" # Create a script to set up SSH for git operations (using secrets mount) RUN echo '#!/bin/bash' > /usr/local/bin/setup-ssh && \ echo 'if [ -f /run/secrets/ssh_private_key ]; then' >> /usr/local/bin/setup-ssh && \ echo ' mkdir -p ~/.ssh' >> /usr/local/bin/setup-ssh && \ echo ' cp /run/secrets/ssh_private_key ~/.ssh/id_rsa' >> /usr/local/bin/setup-ssh && \ echo ' chmod 600 ~/.ssh/id_rsa' >> /usr/local/bin/setup-ssh && \ echo ' ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2>/dev/null' >> /usr/local/bin/setup-ssh && \ echo ' ssh-keyscan -p 2222 -H kankali.darkhelm.lan >> ~/.ssh/known_hosts 2>/dev/null' >> /usr/local/bin/setup-ssh && \ echo 'else' >> /usr/local/bin/setup-ssh && \ echo ' echo "No SSH key provided via secrets mount"' >> /usr/local/bin/setup-ssh && \ echo 'fi' >> /usr/local/bin/setup-ssh && \ chmod +x /usr/local/bin/setup-ssh # Verify that all base tools are working RUN echo "=== Base System Verification ===" && \ python3.14 --version && \ node --version && \ yarn --version && \ uv --version && \ git --version && \ echo "=== Global Development Tools Verification ===" && \ playwright --version && \ tsc --version && \ eslint --version && \ prettier --version && \ /opt/python-dev-tools/bin/ruff --version && \ /opt/python-dev-tools/bin/pyright --version && \ /opt/python-dev-tools/bin/pytest --version && \ /opt/python-dev-tools/bin/pre-commit --version && \ /opt/python-dev-tools/bin/poe --version && \ echo "✓ All base system and development tools verified successfully" # Set working directory WORKDIR /workspace # Default to bash SHELL ["/bin/bash", "-c"] CMD ["/bin/bash"]