Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/guard-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
120 changes: 52 additions & 68 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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)
Expand All @@ -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 \
Expand All @@ -83,86 +101,52 @@ 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

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"]