From dafabd714ccd4292ac61e8bb6dec7447169da164 Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 28 Jan 2026 17:27:17 +0000 Subject: [PATCH] feat: initial core-images repository Consolidated container image definitions for the host-uk ecosystem, producing both Docker images and TIM bundles from a single source. Images: - developer: Full-fat dev environment with Claude Code CLI, PHP 8.4, Node.js, Go, Python, and 100+ tools (ghcr.io/host-uk/core-dev) - server-php: Production Alpine + Nginx + PHP-FPM with multi-stage builds for dev/prod targets (ghcr.io/host-uk/server-php) Includes: - Taskfile for local builds (docker + tim) - GitHub Actions workflow for multi-arch builds - Borgfiles for future TIM bundle generation Consolidates docker-developer and docker-server-php repositories. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/build.yml | 106 ++++++++++++++ .gitignore | 19 +++ README.md | 78 +++++++++++ Taskfile.yaml | 124 +++++++++++++++++ developer/Borgfile | 44 ++++++ developer/Dockerfile | 219 +++++++++++++++++++++++++++++ developer/config/aliases.sh | 56 ++++++++ developer/config/starship.toml | 90 ++++++++++++ server-php/Borgfile | 44 ++++++ server-php/Dockerfile | 245 +++++++++++++++++++++++++++++++++ 10 files changed, 1025 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 Taskfile.yaml create mode 100644 developer/Borgfile create mode 100644 developer/Dockerfile create mode 100644 developer/config/aliases.sh create mode 100644 developer/config/starship.toml create mode 100644 server-php/Borgfile create mode 100644 server-php/Dockerfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..3fcbf7f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,106 @@ +name: Build Images + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + branches: [main] + workflow_dispatch: + +env: + REGISTRY: ghcr.io + +jobs: + # ============================================================ + # Build Docker Images + # ============================================================ + docker: + name: Docker (${{ matrix.image }}) + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + strategy: + matrix: + image: + - developer + - server-php + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/host-uk/${{ matrix.image == 'developer' && 'core-dev' || matrix.image }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./${{ matrix.image }} + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # ============================================================ + # Build TIM Bundles (when core build --type tim is ready) + # ============================================================ + # tim: + # name: TIM (${{ matrix.image }}) + # runs-on: ubuntu-latest + # needs: docker + # + # strategy: + # matrix: + # image: [developer, server-php] + # os: [linux, darwin] + # arch: [amd64, arm64] + # + # steps: + # - uses: actions/checkout@v4 + # + # - name: Install Core + # run: | + # curl -fsSL https://github.com/host-uk/core/releases/latest/download/core-linux-amd64 -o /usr/local/bin/core + # chmod +x /usr/local/bin/core + # + # - name: Build TIM + # run: | + # core build --type tim \ + # --borgfile ./${{ matrix.image }}/Borgfile \ + # --os ${{ matrix.os }} \ + # --arch ${{ matrix.arch }} \ + # -o ./dist/${{ matrix.image }}-${{ matrix.os }}-${{ matrix.arch }}.tim + # + # - name: Upload artifact + # uses: actions/upload-artifact@v4 + # with: + # name: ${{ matrix.image }}-${{ matrix.os }}-${{ matrix.arch }} + # path: ./dist/*.tim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5187bc6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Build outputs +dist/ +*.tim +*.stim + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Temporary +tmp/ +*.tmp +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..1040224 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# core-images + +Container images for the host-uk ecosystem. Each image produces dual outputs: +- **Docker image** → `ghcr.io/host-uk/` +- **TIM bundle** → `--.tim` + +## Images + +| Image | Purpose | Docker | TIM | +|-------|---------|--------|-----| +| `developer` | Full-fat dev environment (100+ tools) | `ghcr.io/host-uk/core-dev` | `core-dev.tim` | +| `server-php` | Alpine + Nginx + PHP-FPM | `ghcr.io/host-uk/server-php` | `server-php.tim` | + +## Usage + +### Docker + +```bash +# Developer environment +docker run -it ghcr.io/host-uk/core-dev + +# PHP server +docker run -p 80:80 ghcr.io/host-uk/server-php +``` + +### TIM (Docker-free via Core) + +```bash +# Install dev environment +core dev install + +# Or run directly +core run core-dev.tim + +# Run PHP server +core run server-php.tim -p 80:80 +``` + +## Building + +```bash +# Requires: task (taskfile.dev) + +# Build all +task build + +# Build specific image +task build:developer +task build:server-php + +# Build TIM only +task build:developer:tim + +# Build Docker only +task build:developer:docker +``` + +## Structure + +``` +core-images/ +├── developer/ # core-dev.tim - Full dev environment +│ ├── Dockerfile +│ ├── Borgfile +│ └── config/ +├── server-php/ # server-php.tim - Nginx + PHP-FPM +│ ├── Dockerfile +│ ├── Borgfile +│ └── config/ +└── base/ # Shared base configurations + └── alpine/ +``` + +## Sources + +Consolidated from: +- [docker-developer](https://github.com/host-uk/docker-developer) +- [docker-server-php](https://github.com/host-uk/docker-server-php) diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..6d06b92 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,124 @@ +version: '3' + +vars: + REGISTRY: ghcr.io/host-uk + VERSION: + sh: git describe --tags --always --dirty 2>/dev/null || echo "dev" + +tasks: + default: + desc: List available tasks + cmds: + - task --list + + # ============================================================ + # Build All + # ============================================================ + build: + desc: Build all images (Docker + TIM) + deps: + - build:developer + - build:server-php + + build:docker: + desc: Build all Docker images + deps: + - build:developer:docker + - build:server-php:docker + + build:tim: + desc: Build all TIM bundles + deps: + - build:developer:tim + - build:server-php:tim + + # ============================================================ + # Developer Image + # ============================================================ + build:developer: + desc: Build developer image (Docker + TIM) + deps: + - build:developer:docker + - build:developer:tim + + build:developer:docker: + desc: Build developer Docker image + dir: developer + cmds: + - | + docker build \ + --tag {{.REGISTRY}}/core-dev:{{.VERSION}} \ + --tag {{.REGISTRY}}/core-dev:latest \ + --build-arg VERSION={{.VERSION}} \ + . + + build:developer:tim: + desc: Build developer TIM bundle + dir: developer + cmds: + - echo "TODO: core build --type tim -o ../dist/core-dev-{{OS}}-{{ARCH}}.tim" + + push:developer: + desc: Push developer image to registry + cmds: + - docker push {{.REGISTRY}}/core-dev:{{.VERSION}} + - docker push {{.REGISTRY}}/core-dev:latest + + # ============================================================ + # Server PHP Image + # ============================================================ + build:server-php: + desc: Build server-php image (Docker + TIM) + deps: + - build:server-php:docker + - build:server-php:tim + + build:server-php:docker: + desc: Build server-php Docker image + dir: server-php + cmds: + - | + docker build \ + --tag {{.REGISTRY}}/server-php:{{.VERSION}} \ + --tag {{.REGISTRY}}/server-php:latest \ + --build-arg VERSION={{.VERSION}} \ + . + + build:server-php:tim: + desc: Build server-php TIM bundle + dir: server-php + cmds: + - echo "TODO: core build --type tim -o ../dist/server-php-{{OS}}-{{ARCH}}.tim" + + push:server-php: + desc: Push server-php image to registry + cmds: + - docker push {{.REGISTRY}}/server-php:{{.VERSION}} + - docker push {{.REGISTRY}}/server-php:latest + + # ============================================================ + # Release + # ============================================================ + release: + desc: Build and push all images + cmds: + - task: build + - task: push:developer + - task: push:server-php + + # ============================================================ + # Utilities + # ============================================================ + clean: + desc: Remove build artifacts + cmds: + - rm -rf dist/ + - docker rmi {{.REGISTRY}}/core-dev:{{.VERSION}} || true + - docker rmi {{.REGISTRY}}/server-php:{{.VERSION}} || true + + dist: + desc: Create dist directory + cmds: + - mkdir -p dist + status: + - test -d dist diff --git a/developer/Borgfile b/developer/Borgfile new file mode 100644 index 0000000..20afb81 --- /dev/null +++ b/developer/Borgfile @@ -0,0 +1,44 @@ +# Borgfile for core-dev TIM bundle +# This will be processed by: core build --borgfile -o core-dev.tim + +# Base from the Docker build output or direct Alpine +FROM alpine:3.22 + +# Core binary (the orchestrator) +ADD https://github.com/host-uk/core/releases/latest/download/core-linux-${ARCH} /usr/local/bin/core + +# ============================================================ +# AI / LLM Assistants +# ============================================================ +ADD https://github.com/anthropics/claude-code/releases/latest/download/claude-linux-${ARCH} /usr/local/bin/claude +# ADD gemini, aider, ollama, llm... + +# ============================================================ +# Version Control +# ============================================================ +ADD https://github.com/cli/cli/releases/latest/download/gh_linux_${ARCH}.tar.gz /tmp/gh.tar.gz +# Extract and install gh... + +# ============================================================ +# Languages / Runtimes +# ============================================================ +# FrankenPHP - embedded PHP runtime +ADD https://github.com/dunglas/frankenphp/releases/latest/download/frankenphp-linux-${ARCH} /usr/local/bin/frankenphp + +# Node.js +ADD https://nodejs.org/dist/latest-lts/node-linux-${ARCH}.tar.xz /tmp/node.tar.xz + +# Go, Python, Rust, etc... + +# ============================================================ +# Configuration +# ============================================================ +ADD config/zshrc /etc/skel/.zshrc +ADD config/starship.toml /etc/skel/.config/starship.toml +ADD config/aliases.sh /etc/profile.d/aliases.sh + +# ============================================================ +# OCI Config +# ============================================================ +# Generated automatically by core build +# Sets up proper entrypoint, env vars, capabilities diff --git a/developer/Dockerfile b/developer/Dockerfile new file mode 100644 index 0000000..c538b4f --- /dev/null +++ b/developer/Dockerfile @@ -0,0 +1,219 @@ +# ============================================================ +# Docker Developer - Claude-Focused Alpine Dev Image +# +# A kitchen-sink developer environment optimized for +# AI-assisted development with Claude Code CLI. +# +# Build: docker build -t docker-developer . +# Run: docker run -it docker-developer +# ============================================================ + +ARG ALPINE_VERSION=3.22 + +# ============================================================ +# Developer - Full development environment +# ============================================================ +FROM alpine:${ALPINE_VERSION} AS developer + +LABEL maintainer="Snider " +LABEL org.opencontainers.image.source="https://github.com/host-uk/docker-developer" +LABEL org.opencontainers.image.description="Claude-focused Alpine developer environment" +LABEL org.opencontainers.image.licenses="EUPL-1.2" +LABEL org.opencontainers.image.vendor="Host UK" +LABEL org.opencontainers.image.title="Docker Developer" + +# Environment variables +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 +ENV TERM=xterm-256color +ENV EDITOR=vim +ENV SHELL=/bin/zsh +ENV GOPATH=/root/go +ENV PATH="/root/go/bin:/root/.local/bin:/root/.composer/vendor/bin:/usr/local/bin:${PATH}" + +# ============================================================ +# Core System Tools +# ============================================================ +RUN apk add --no-cache \ + # Shell and basics + bash \ + zsh \ + zsh-vcs \ + curl \ + wget \ + ca-certificates \ + # Build tools + make \ + cmake \ + ninja \ + meson \ + gcc \ + g++ \ + musl-dev \ + linux-headers \ + # Essential utilities + git \ + git-lfs \ + openssh-client \ + gnupg \ + less \ + tree \ + ncdu \ + htop \ + # Editors + vim \ + nano \ + # Text processing + jq \ + yq \ + # Search tools + ripgrep \ + fzf \ + # Modern CLI tools (from Alpine packages) + bat \ + eza \ + fd \ + # Terminal multiplexer + tmux \ + # Compression + zip \ + unzip \ + tar \ + gzip \ + xz \ + # Networking + bind-tools \ + iputils \ + # Process management + shadow \ + # Template processing + gettext \ + # For starship + starship + +# ============================================================ +# Node.js Ecosystem +# ============================================================ +RUN apk add --no-cache \ + nodejs \ + npm + +# Install global Node.js packages +RUN npm install -g \ + @anthropic-ai/claude-code \ + typescript \ + ts-node \ + pnpm \ + yarn + +# ============================================================ +# Python Ecosystem +# ============================================================ +RUN apk add --no-cache \ + python3 \ + py3-pip \ + python3-dev + +# Install Python tools +RUN pip3 install --break-system-packages \ + pipx \ + uv \ + ipython \ + httpie + +# ============================================================ +# PHP Ecosystem +# ============================================================ +RUN apk add --no-cache \ + php84 \ + php84-phar \ + php84-mbstring \ + php84-openssl \ + php84-curl \ + php84-iconv \ + php84-tokenizer \ + php84-dom \ + php84-xml \ + php84-xmlwriter \ + php84-simplexml \ + php84-ctype \ + php84-fileinfo \ + php84-json \ + php84-posix \ + php84-pcntl \ + php84-zip + +# Create php symlink +RUN ln -sf /usr/bin/php84 /usr/bin/php + +# Install Composer +RUN curl -sS https://getcomposer.org/installer | php -- \ + --install-dir=/usr/bin --filename=composer + +# Install PHP dev tools globally +RUN composer global require --no-interaction \ + phpunit/phpunit:^11.0 \ + phpstan/phpstan:^2.0 \ + squizlabs/php_codesniffer:^3.0 \ + friendsofphp/php-cs-fixer:^3.0 + +# ============================================================ +# Go Ecosystem +# ============================================================ +RUN apk add --no-cache go + +# Install Go tools (pinned to versions compatible with Go 1.24) +RUN go install golang.org/x/tools/gopls@v0.17.1 && \ + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2 + +# ============================================================ +# Database Clients +# ============================================================ +RUN apk add --no-cache \ + postgresql16-client \ + mariadb-client \ + redis \ + sqlite + +# ============================================================ +# Container Tools +# ============================================================ +RUN apk add --no-cache docker-cli + +# ============================================================ +# Git Enhancements +# ============================================================ +RUN apk add --no-cache github-cli lazygit + +# ============================================================ +# Oh-My-Zsh Installation +# ============================================================ +RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended + +# Install zsh plugins +RUN git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-/root/.oh-my-zsh/custom}/plugins/zsh-autosuggestions && \ + git clone https://github.com/zsh-users/zsh-syntax-highlighting ${ZSH_CUSTOM:-/root/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting + +# ============================================================ +# Configuration Files +# ============================================================ +COPY --chmod=644 config/zshrc /root/.zshrc +COPY --chmod=644 config/starship.toml /root/.config/starship.toml +COPY --chmod=644 config/tmux.conf /root/.tmux.conf + +# ============================================================ +# Entrypoint and Hooks +# ============================================================ +COPY --chmod=755 scripts/entrypoint.sh /usr/local/bin/entrypoint.sh +COPY --chmod=755 scripts/hooks.sh /usr/local/bin/hooks.sh + +# Create config directories +RUN mkdir -p /root/.config/docker-developer/hooks/pre-start \ + /root/.config/docker-developer/hooks/post-start \ + /root/.claude \ + /workspace + +WORKDIR /workspace + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] +CMD ["/bin/zsh"] diff --git a/developer/config/aliases.sh b/developer/config/aliases.sh new file mode 100644 index 0000000..220f46f --- /dev/null +++ b/developer/config/aliases.sh @@ -0,0 +1,56 @@ +# Core-dev shell aliases + +# Navigation +alias ..='cd ..' +alias ...='cd ../..' +alias ll='eza -la --icons --git' +alias la='eza -la --icons' +alias lt='eza --tree --level=2 --icons' + +# Git +alias g='git' +alias gs='git status' +alias gd='git diff' +alias gc='git commit' +alias gp='git push' +alias gl='git log --oneline -20' +alias gco='git checkout' +alias gb='git branch' +alias lg='lazygit' + +# Docker +alias d='docker' +alias dc='docker compose' +alias dps='docker ps' +alias di='docker images' +alias dex='docker exec -it' +alias ld='lazydocker' + +# Kubernetes +alias k='kubectl' +alias kgp='kubectl get pods' +alias kgs='kubectl get svc' +alias kgd='kubectl get deployments' + +# PHP/Laravel +alias art='php artisan' +alias sail='./vendor/bin/sail' +alias pest='./vendor/bin/pest' +alias pint='./vendor/bin/pint' + +# Core +alias c='core' +alias cdev='core dev' +alias cbuild='core build' +alias crun='core run' + +# Misc +alias cat='bat' +alias find='fd' +alias grep='rg' +alias top='btop' +alias vim='nvim' +alias vi='nvim' + +# FZF integrations +alias fzp='fzf --preview "bat --color=always {}"' diff --git a/developer/config/starship.toml b/developer/config/starship.toml new file mode 100644 index 0000000..0c02f72 --- /dev/null +++ b/developer/config/starship.toml @@ -0,0 +1,90 @@ +# Starship prompt configuration for core-dev +# https://starship.rs/config/ + +format = """ +$username\ +$hostname\ +$directory\ +$git_branch\ +$git_status\ +$php\ +$nodejs\ +$golang\ +$python\ +$rust\ +$docker_context\ +$kubernetes\ +$cmd_duration\ +$line_break\ +$character""" + +[character] +success_symbol = "[❯](bold green)" +error_symbol = "[❯](bold red)" + +[username] +style_user = "bold blue" +format = "[$user]($style) " +show_always = false + +[hostname] +ssh_only = true +format = "@ [$hostname](bold yellow) " + +[directory] +truncation_length = 3 +truncate_to_repo = true +style = "bold cyan" + +[git_branch] +symbol = " " +style = "bold purple" +format = "[$symbol$branch]($style) " + +[git_status] +format = '([\[$all_status$ahead_behind\]]($style) )' +style = "bold red" + +[php] +symbol = " " +format = "[$symbol($version )]($style)" +style = "147" + +[nodejs] +symbol = " " +format = "[$symbol($version )]($style)" +style = "bold green" + +[golang] +symbol = " " +format = "[$symbol($version )]($style)" +style = "bold cyan" + +[python] +symbol = " " +format = "[$symbol($version )]($style)" +style = "bold yellow" + +[rust] +symbol = " " +format = "[$symbol($version )]($style)" +style = "bold red" + +[docker_context] +symbol = " " +format = "[$symbol$context]($style) " +style = "blue bold" +only_with_files = true + +[kubernetes] +symbol = "☸ " +format = '[$symbol$context( \($namespace\))]($style) ' +style = "cyan bold" +disabled = false + +[cmd_duration] +min_time = 2_000 +format = "took [$duration](bold yellow) " + +[line_break] +disabled = false diff --git a/server-php/Borgfile b/server-php/Borgfile new file mode 100644 index 0000000..ec152d5 --- /dev/null +++ b/server-php/Borgfile @@ -0,0 +1,44 @@ +# Borgfile for server-php TIM bundle +# This will be processed by: core build --borgfile -o server-php.tim + +# Base from Alpine +FROM alpine:3.22 + +# ============================================================ +# PHP Runtime (FrankenPHP or PHP-FPM) +# ============================================================ +ADD https://github.com/dunglas/frankenphp/releases/latest/download/frankenphp-linux-${ARCH} /usr/local/bin/frankenphp + +# Or traditional PHP-FPM setup +# ADD php${PHP_VERSION} /usr/bin/php +# ADD php${PHP_VERSION}-fpm /usr/sbin/php-fpm + +# ============================================================ +# Nginx (optional, FrankenPHP has Caddy built-in) +# ============================================================ +# ADD nginx /usr/sbin/nginx +# ADD config/nginx/ /etc/nginx/ + +# ============================================================ +# PHP Extensions +# ============================================================ +# Common extensions bundled with FrankenPHP +# Additional extensions via PHP_EXTENSIONS env var + +# ============================================================ +# Configuration +# ============================================================ +ADD config/php-fpm/ /etc/php/ +ADD config/supervisor/ /etc/supervisor/ + +# ============================================================ +# Application Skeleton +# ============================================================ +ADD product/ /var/www/html/ + +# ============================================================ +# OCI Config +# ============================================================ +# Exposes: 80 (HTTP), 443 (HTTPS), 8080 (WebSocket/Reverb) +# User: www-data +# Entrypoint: frankenphp run --config /etc/caddy/Caddyfile diff --git a/server-php/Dockerfile b/server-php/Dockerfile new file mode 100644 index 0000000..a0e99ca --- /dev/null +++ b/server-php/Dockerfile @@ -0,0 +1,245 @@ +# ============================================================ +# Multi-stage Dockerfile for Alpine + Nginx + PHP-FPM +# Supports dynamic PHP versions based on Alpine version +# +# Build targets: +# - builder: Composer install and asset building +# - runtime: Base runtime with PHP and Nginx +# - development: Dev tools (xdebug, phpunit, profiling) +# - production: Hardened production image (default) +# ============================================================ + +# Build arguments for version control +ARG ALPINE_VERSION=3.22 +ARG PHP_VERSION=84 + +# ============================================================ +# Stage 1: Builder - Install dependencies and build assets +# ============================================================ +FROM alpine:${ALPINE_VERSION} AS builder + +ARG PHP_VERSION +ENV PHP_VERSION=${PHP_VERSION} + +# Install build dependencies +RUN apk add --no-cache \ + git \ + curl \ + php${PHP_VERSION} \ + php${PHP_VERSION}-phar \ + php${PHP_VERSION}-mbstring \ + php${PHP_VERSION}-openssl \ + php${PHP_VERSION}-curl \ + php${PHP_VERSION}-iconv \ + php${PHP_VERSION}-tokenizer + +# Create php symlink +RUN ln -s /usr/bin/php${PHP_VERSION} /usr/bin/php + +# Install Composer +RUN curl -sS https://getcomposer.org/installer | php -- \ + --install-dir=/usr/bin --filename=composer + +WORKDIR /build + +# Copy application code +COPY product/ ./ + +# Install dependencies (only if composer.json exists) +RUN if [ -f composer.json ]; then \ + composer install \ + --no-dev \ + --optimize-autoloader \ + --no-interaction \ + --no-progress \ + --prefer-dist; \ + fi +COPY patch/ ./ + +# ============================================================ +# Stage 2: Runtime - Base image with PHP and Nginx +# ============================================================ +FROM alpine:${ALPINE_VERSION} AS runtime + +ARG PHP_VERSION +ENV PHP_VERSION=${PHP_VERSION} +ENV PHP_INI_DIR=/etc/php${PHP_VERSION} +ENV APP_ENV=production + +LABEL maintainer="Snider " +LABEL org.opencontainers.image.source="https://github.com/host-uk/docker-server-php" +LABEL org.opencontainers.image.description="Production-ready Alpine+Nginx+PHP-FPM base image" +LABEL org.opencontainers.image.licenses="EUPL-1.2" +LABEL org.opencontainers.image.vendor="Host UK" +LABEL org.opencontainers.image.title="Docker Server PHP" +LABEL org.opencontainers.image.documentation="https://github.com/host-uk/docker-server-php" + +# Install only runtime dependencies +RUN apk add --no-cache \ + nginx \ + nginx-mod-http-brotli \ + php${PHP_VERSION} \ + php${PHP_VERSION}-bcmath \ + php${PHP_VERSION}-ctype \ + php${PHP_VERSION}-curl \ + php${PHP_VERSION}-dom \ + php${PHP_VERSION}-exif \ + php${PHP_VERSION}-fileinfo \ + php${PHP_VERSION}-fpm \ + php${PHP_VERSION}-gd \ + php${PHP_VERSION}-iconv \ + php${PHP_VERSION}-intl \ + php${PHP_VERSION}-mbstring \ + php${PHP_VERSION}-mysqli \ + php${PHP_VERSION}-opcache \ + php${PHP_VERSION}-openssl \ + php${PHP_VERSION}-pdo \ + php${PHP_VERSION}-pdo_mysql \ + php${PHP_VERSION}-phar \ + php${PHP_VERSION}-posix \ + php${PHP_VERSION}-redis \ + php${PHP_VERSION}-session \ + php${PHP_VERSION}-simplexml \ + php${PHP_VERSION}-sodium \ + php${PHP_VERSION}-tokenizer \ + php${PHP_VERSION}-xml \ + php${PHP_VERSION}-xmlreader \ + php${PHP_VERSION}-xmlwriter \ + php${PHP_VERSION}-zip \ + supervisor \ + curl \ + ca-certificates \ + gettext + +# Create php symlink +RUN ln -s /usr/bin/php${PHP_VERSION} /usr/bin/php + +WORKDIR /var/www/html + +# Copy built application from builder +COPY --chmod=755 --chown=nobody:nobody --from=builder /build /var/www/html + +# Copy configuration templates +COPY --chmod=644 --chown=nobody:nobody config/nginx.conf /etc/nginx/nginx.conf +COPY --chmod=755 --chown=nobody:nobody config/conf.d /etc/nginx/conf.d/ +COPY --chmod=644 --chown=nobody:nobody config/fpm-pool.conf.template ${PHP_INI_DIR}/php-fpm.d/www.conf.template +COPY --chmod=644 --chown=nobody:nobody config/php.ini.template ${PHP_INI_DIR}/conf.d/custom.ini.template +# Create supervisor directory with proper permissions and copy config +RUN mkdir -p /etc/supervisor/conf.d +COPY --chmod=644 config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +# Copy and set up entrypoint +COPY --chmod=755 --chown=nobody:nobody scripts/entrypoint.sh /usr/local/bin/entrypoint.sh + +# Set permissions for system directories +RUN chown -R nobody:nobody /run /var/lib/nginx /var/log/nginx ${PHP_INI_DIR} + +USER nobody + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD curl --silent --fail http://127.0.0.1/health || exit 1 + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] + +# ============================================================ +# Stage 3: Development - Full dev environment with debugging +# ============================================================ +FROM runtime AS development + +ARG PHP_VERSION +ENV PHP_VERSION=${PHP_VERSION} +ENV PHP_INI_DIR=/etc/php${PHP_VERSION} +ENV APP_ENV=development +ENV XDEBUG_MODE=develop,debug,coverage + +USER root + +# Install development tools +RUN apk add --no-cache \ + php${PHP_VERSION}-xdebug \ + php${PHP_VERSION}-phpdbg \ + php${PHP_VERSION}-pecl-pcov \ + git \ + make \ + bash \ + vim \ + nano + +# Install Composer in dev image +RUN curl -sS https://getcomposer.org/installer | php -- \ + --install-dir=/usr/bin --filename=composer + +# Copy xdebug configuration +COPY --chmod=644 config/xdebug.ini ${PHP_INI_DIR}/conf.d/50_xdebug.ini + +# Copy development php.ini overrides +COPY --chmod=644 config/php-dev.ini ${PHP_INI_DIR}/conf.d/60_development.ini + +# Install PHPUnit, PHPStan, PHP_CodeSniffer globally +RUN composer global require --no-interaction \ + phpunit/phpunit:^11.0 \ + phpstan/phpstan:^2.0 \ + squizlabs/php_codesniffer:^3.0 \ + friendsofphp/php-cs-fixer:^3.0 + +# Add composer bin to PATH +ENV PATH="/root/.composer/vendor/bin:${PATH}" + +# Reset permissions +RUN chown -R nobody:nobody /run /var/lib/nginx /var/log/nginx ${PHP_INI_DIR} + +USER nobody + +# Override healthcheck for development (more lenient) +HEALTHCHECK --interval=60s --timeout=30s --start-period=10s --retries=5 \ + CMD curl --silent --fail http://127.0.0.1/health || exit 1 + +# ============================================================ +# Stage 4: Production - Hardened, optimized production image +# ============================================================ +FROM runtime AS production + +ARG PHP_VERSION +ENV PHP_VERSION=${PHP_VERSION} +ENV PHP_INI_DIR=/etc/php${PHP_VERSION} +ENV APP_ENV=production + +USER root + +# Copy production-optimized configurations +COPY --chmod=644 config/opcache-prod.ini ${PHP_INI_DIR}/conf.d/10_opcache_prod.ini +COPY --chmod=644 config/php-prod.ini ${PHP_INI_DIR}/conf.d/60_production.ini +COPY --chmod=644 config/nginx-performance.conf /etc/nginx/conf.d/performance.conf + +# Security hardening +RUN set -eux; \ + # Remove unnecessary packages + apk del --no-cache \ + fortify-headers \ + apk-tools 2>/dev/null || true; \ + # Remove package cache + rm -rf /var/cache/apk/* /tmp/* /var/tmp/*; \ + # Remove shell history + rm -f /root/.ash_history /root/.bash_history 2>/dev/null || true; \ + # Set restrictive permissions on sensitive directories + chmod 700 /root 2>/dev/null || true; \ + # Remove crontabs + rm -rf /var/spool/cron /etc/crontabs /etc/periodic 2>/dev/null || true; \ + # Remove unnecessary user accounts + sed -i -r '/^(nobody|root)/!d' /etc/passwd 2>/dev/null || true; \ + sed -i -r '/^(nobody|root)/!d' /etc/shadow 2>/dev/null || true; \ + sed -i -r '/^(nobody|root|nogroup)/!d' /etc/group 2>/dev/null || true; \ + # Remove interactive shells for system users + sed -i -r 's#^(.*):[^:]*$#\1:/sbin/nologin#' /etc/passwd 2>/dev/null || true + +# Reset permissions +RUN chown -R nobody:nobody /run /var/lib/nginx /var/log/nginx ${PHP_INI_DIR} + +USER nobody + +# Production healthcheck (strict) +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD curl --silent --fail http://127.0.0.1/health || exit 1