99 lines
5.0 KiB
Docker
99 lines
5.0 KiB
Docker
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Stage 1 — npm install (Tailwind CLI)
|
|
# The .NET SDK stage needs node_modules present before running dotnet publish
|
|
# because the MSBuild Tailwind target calls `npx @tailwindcss/cli` during build.
|
|
#
|
|
# We use the official node:24-slim image here. This means the npm that ships
|
|
# with Node 24 (npm 10.x) is used as-is — we deliberately do NOT run
|
|
# `npm install -g npm@latest` anywhere. Running a global npm self-upgrade
|
|
# inside a Debian container is a known reliability hazard: npm replaces its
|
|
# own running binaries mid-flight, which can cause EBUSY / ENOENT failures
|
|
# that corrupt the install. The bundled npm is current enough.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
FROM node:24-slim AS npm-install
|
|
|
|
WORKDIR /npm
|
|
COPY Htmx.ApiDemo/package.json .
|
|
# npm ci requires package-lock.json; if it doesn't exist yet, run
|
|
# `npm install` locally first to generate it, then commit it to the repo.
|
|
COPY Htmx.ApiDemo/package-lock.json* ./
|
|
# ci is preferred over install in CI/Docker contexts: respects package-lock,
|
|
# clean installs, and is faster.
|
|
RUN npm ci
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Stage 2 — AOT publish
|
|
# Uses the full .NET 10 SDK image. Node/npx must also be present here so the
|
|
# Tailwind MSBuild target can run. We install Node 24 from NodeSource using
|
|
# the official setup script and then immediately install nodejs via apt — no
|
|
# subsequent `npm install -g npm` step, for the same reason as above.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS publish
|
|
|
|
# Install Node.js 24 (required by the Tailwind MSBuild target at publish time).
|
|
# We download the NodeSource setup script to a file first so we can inspect it
|
|
# if needed, then run it. Using `| bash -` directly is convenient but hides
|
|
# the script from audit — the two-step form is safer in CI/CD contexts.
|
|
RUN apt-get update && \
|
|
apt-get install -y --no-install-recommends curl ca-certificates && \
|
|
curl -fsSL https://deb.nodesource.com/setup_24.x -o /tmp/nodesource_setup.sh && \
|
|
bash /tmp/nodesource_setup.sh && \
|
|
apt-get install -y --no-install-recommends nodejs && \
|
|
rm /tmp/nodesource_setup.sh && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
# Intentionally no `npm install -g npm` — see Stage 1 note above.
|
|
|
|
WORKDIR /src
|
|
|
|
# Copy solution and project files first so NuGet restore is cached separately
|
|
COPY Htmx.slnx .
|
|
COPY Htmx.ApiDemo/Htmx.ApiDemo.csproj Htmx.ApiDemo/
|
|
COPY Htmx.SourceGenerator/Htmx.SourceGenerator.csproj Htmx.SourceGenerator/
|
|
|
|
RUN dotnet restore Htmx.ApiDemo/Htmx.ApiDemo.csproj
|
|
|
|
# Bring in the pre-installed node_modules from Stage 1.
|
|
# These were installed with `npm ci` on Node 24 — no npm upgrade was performed.
|
|
COPY --from=npm-install /npm/node_modules Htmx.ApiDemo/node_modules
|
|
|
|
# Copy the rest of the source
|
|
COPY . .
|
|
|
|
# AOT publish — output goes to /publish
|
|
RUN dotnet publish Htmx.ApiDemo/Htmx.ApiDemo.csproj \
|
|
-c Release \
|
|
--no-restore \
|
|
-o /publish
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Stage 3 — Runtime image
|
|
# runtime-deps provides the native library dependencies (libc, libssl, libicu)
|
|
# that the AOT binary links against at runtime — no .NET runtime needed.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0 AS runtime
|
|
|
|
# Run as non-root for security hardening (recommended by Cloud Run docs)
|
|
RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
|
|
|
|
WORKDIR /app
|
|
|
|
COPY --from=publish /publish .
|
|
|
|
# Ensure the binary is executable
|
|
RUN chmod +x ./Htmx.ApiDemo
|
|
|
|
# Cloud Run injects PORT (default 8080).
|
|
# ASP.NET Core reads ASPNETCORE_HTTP_PORTS, not PORT directly, so we set it.
|
|
# The entrypoint script below maps $PORT → ASPNETCORE_HTTP_PORTS at startup.
|
|
COPY GCR/entrypoint.sh /entrypoint.sh
|
|
RUN chmod +x /entrypoint.sh
|
|
|
|
# Transfer ownership so the app can write temp files if needed
|
|
RUN chown -R appuser:appgroup /app
|
|
|
|
USER appuser
|
|
|
|
EXPOSE 8080
|
|
|
|
ENTRYPOINT ["/entrypoint.sh"]
|