From 865d29e49312e8f836f2959f763e86d8cfef74e7 Mon Sep 17 00:00:00 2001 From: rmie <17530606+rmie@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:17:54 +0200 Subject: [PATCH 1/2] refactor(docker): optimize build architecture and security - Invert base image to use Playwright Noble for native browser support - Install Temurin 21 JDK directly from Adoptium APT repository - Extract Rust toolchain from official image via multi-stage COPY - Relocate appuser creation to preempt UID 1000 conflicts - Update HEALTHCHECK to use Spring Boot liveness probe --- Dockerfile | 120 +++++++++++++++++++++++------------------------------ 1 file changed, 52 insertions(+), 68 deletions(-) diff --git a/Dockerfile b/Dockerfile index c58f40d..cd69adf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,28 +12,43 @@ COPY src ./src RUN mvn clean package -DskipTests -o # --- Runtime image --- -# -# We use Ubuntu Noble (24.04 LTS, glibc) here — NOT Alpine — because the -# PR-workflow E2E test runners (Playwright + Cypress) ship browser binaries -# that are dynamically linked against glibc and do not work on musl-based -# distros. Noble also gives us a newer toolchain (gcc 13, Python 3.12, …) -# than the previous Jammy base. -FROM eclipse-temurin:21-jre-noble +# Playwright Noble (Ubuntu 24.04 LTS) provides pre-configured Chromium/Node +# dependencies and a modern glibc baseline (2.39) that ensures backward +# compatibility when copying binaries from older base images (e.g. Bookworm). +FROM mcr.microsoft.com/playwright:v1.60.0-noble ENV DEBIAN_FRONTEND=noninteractive \ PIP_BREAK_SYSTEM_PACKAGES=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ PIP_NO_CACHE_DIR=1 \ - CYPRESS_CACHE_FOLDER=/home/appuser/.cache/Cypress \ - PLAYWRIGHT_BROWSERS_PATH=/home/appuser/.cache/ms-playwright \ + CYPRESS_CACHE_FOLDER=/opt/cypress-cache \ + PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \ NPM_CONFIG_PREFIX=/usr/local \ NODE_PATH=/usr/local/lib/node_modules +# --------------------------------------------------------------------------- +# App user +# Claim UID/GID 1000 early before apt packages are installed. +# +# TODO(security): Consider failing the build if UID/GID 1000 is already taken +# instead of deleting the existing user, to avoid breaking base image assumptions. +# --------------------------------------------------------------------------- +RUN set -eux; \ + if getent passwd 1000 >/dev/null; then \ + existing_user="$(getent passwd 1000 | cut -d: -f1)"; \ + userdel -r "$existing_user" 2>/dev/null || userdel "$existing_user"; \ + fi; \ + if getent group 1000 >/dev/null; then \ + existing_group="$(getent group 1000 | cut -d: -f1)"; \ + groupdel "$existing_group" || true; \ + fi; \ + groupadd -g 1000 appgroup; \ + useradd -m -u 1000 -g appgroup -s /bin/bash appuser; \ + mkdir -p /app /app/prompts /opt/cypress-cache; \ + chown -R appuser:appgroup /app /home/appuser /opt/cypress-cache + # --------------------------------------------------------------------------- # Base utilities + AI-agent build toolchain -# (Java/Maven, Python, Go, C/C++, Ruby — Node.js, .NET, k6 and Rust are -# installed below from upstream channels to get current versions and avoid -# the patchy Ubuntu repos.) # --------------------------------------------------------------------------- RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates curl wget git bash gnupg lsb-release apt-transport-https \ @@ -47,13 +62,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* # --------------------------------------------------------------------------- -# Node.js 22 LTS (NodeSource) — required by Playwright, Cypress and the -# scaffolded `npx` commands the PR-workflow tools issue. +# Eclipse Temurin 21 JDK (Adoptium Official Repo) # --------------------------------------------------------------------------- -RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ - apt-get install -y --no-install-recommends nodejs && \ - rm -rf /var/lib/apt/lists/* && \ - node --version && npm --version +RUN wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/trusted.gpg.d/adoptium.gpg > /dev/null && \ + echo "deb https://packages.adoptium.net/artifactory/deb noble main" | tee /etc/apt/sources.list.d/adoptium.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends temurin-21-jdk && \ + rm -rf /var/lib/apt/lists/* + +ENV JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64 +ENV PATH="${JAVA_HOME}/bin:${PATH}" # --------------------------------------------------------------------------- # .NET 10 SDK (Microsoft package feed) @@ -66,7 +84,7 @@ RUN wget -q https://packages.microsoft.com/config/ubuntu/24.04/packages-microsof dotnet --info # --------------------------------------------------------------------------- -# k6 (Grafana APT repo) — used by the PR-workflow `k6` test framework. +# k6 (Grafana APT repo) # --------------------------------------------------------------------------- RUN gpg -k && \ gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \ @@ -83,73 +101,41 @@ RUN gpg -k && \ RUN pip3 install --no-cache-dir pytest requests && \ pytest --version -# --------------------------------------------------------------------------- -# App user — UID/GID 1000 are conventional but some base images (Ubuntu 23.04+, -# certain Eclipse Temurin variants) ship a pre-existing `ubuntu` account at -# 1000:1000. Remove that placeholder before recreating our own `appuser` so -# the build does not fail with `groupadd: GID '1000' already exists`. -# --------------------------------------------------------------------------- -RUN set -eux; \ - if getent passwd 1000 >/dev/null; then \ - existing_user="$(getent passwd 1000 | cut -d: -f1)"; \ - userdel -r "$existing_user" 2>/dev/null || userdel "$existing_user"; \ - fi; \ - if getent group 1000 >/dev/null; then \ - existing_group="$(getent group 1000 | cut -d: -f1)"; \ - groupdel "$existing_group" || true; \ - fi; \ - groupadd -g 1000 appgroup; \ - useradd -m -u 1000 -g appgroup -s /bin/bash appuser; \ - mkdir -p /app /app/prompts; \ - chown -R appuser:appgroup /app /home/appuser - # --------------------------------------------------------------------------- # Playwright + Cypress — installed GLOBALLY under /usr/local/lib/node_modules -# so that the per-PR scaffolded `playwright.config.ts` can resolve the bare -# specifier `@playwright/test` (and Cypress respectively) via NODE_PATH -# WITHOUT requiring an `npm install` inside every PR workspace. -# -# `playwright install-deps` (run as root) installs the APT packages Playwright -# needs (fonts, libnss, libasound, …). `playwright install` (run as appuser) -# then drops the browser binaries into PLAYWRIGHT_BROWSERS_PATH inside the -# user's home so the JVM (running as appuser) can find them at run time. +# The Playwright base image already has browsers at /ms-playwright, so we +# don't need to run install-deps or playwright install. # --------------------------------------------------------------------------- +ENV CI=1 RUN npm install -g --omit=optional \ @playwright/test@1.60.0 \ playwright@1.60.0 \ cypress@15 && \ - playwright install-deps chromium && \ - rm -rf /var/lib/apt/lists/* && \ - chown -R appuser:appgroup /home/appuser - -USER appuser -RUN playwright install chromium && \ - cypress install + cypress install && \ + chown -R appuser:appgroup /opt/cypress-cache # --------------------------------------------------------------------------- -# Rust (optional, mirrors previous image) +# Rust +# Copied from the official image to avoid `curl | sh` supply chain risks. +# The 'bookworm' tag is used as its glibc baseline (2.36) safely predates +# Noble's (2.39), guaranteeing backward compatibility. # --------------------------------------------------------------------------- -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal -ENV PATH="/home/appuser/.cargo/bin:${PATH}" +ENV RUSTUP_HOME=/opt/rustup \ + CARGO_HOME=/opt/cargo \ + PATH="/opt/cargo/bin:${PATH}" +COPY --from=rust:1-slim-bookworm --chown=appuser:appgroup /usr/local/cargo /opt/cargo +COPY --from=rust:1-slim-bookworm --chown=appuser:appgroup /usr/local/rustup /opt/rustup USER root -# JAVA_HOME for Maven invocations done by the build-validation agents. -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH="${JAVA_HOME}/bin:${PATH}" - # Go path ENV GOPATH=/home/appuser/go ENV PATH="${GOPATH}/bin:/usr/lib/go/bin:${PATH}" WORKDIR /app COPY --from=build --chown=appuser:appgroup /app/target/*.jar app.jar - -# Copy prompts directory - these serve as defaults -# They can be overridden by mounting a volume at runtime COPY --chown=appuser:appgroup prompts/ /app/prompts/ -# Verify prompts exist (fail build if missing) RUN test -f /app/prompts/default.md && echo "Prompts verified" USER appuser @@ -157,12 +143,10 @@ USER appuser EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ - CMD curl -sf http://localhost:8080/actuator/health || exit 1 + CMD curl -sf http://localhost:8080/actuator/health/liveness || exit 1 ENTRYPOINT ["/usr/bin/tini", "--", "java", \ "-XX:+UseContainerSupport", \ "-XX:MaxRAMPercentage=75.0", \ "-jar", "app.jar", \ "--spring.profiles.active=docker"] - - From 295d0fe81889ddb52f03794168ffc2fceae8312e Mon Sep 17 00:00:00 2001 From: rmie <17530606+rmie@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:48:14 +0200 Subject: [PATCH 2/2] build(ci): upgrade github actions to v5 to fix Node 20 deprecation warning --- .github/workflows/ci.yml | 4 ++-- .github/workflows/docker-publish.yml | 2 +- .github/workflows/guard-main.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f43d102..7408fba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Java 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: '21' diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 8e1149f..9d7c3d9 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/guard-main.yml b/.github/workflows/guard-main.yml index 92ceb2d..66f2591 100644 --- a/.github/workflows/guard-main.yml +++ b/.github/workflows/guard-main.yml @@ -36,7 +36,7 @@ jobs: echo "OK: branch name starts with 'release/'." - name: Checkout PR head - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 1