# syntax=docker/dockerfile:1.7 FROM alpine:3.23@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40 AS emulator ARG EMULATORJS_VERSION=4.2.3 ARG EMULATORJS_SHA256=07d451bc06fa3ad04ab30d9b94eb63ac34ad0babee52d60357b002bde8f3850b COPY .build-inputs/emulatorjs/ /tmp/emulatorjs-input/ RUN fetch() { \ url="$1"; destination="$2"; attempt=1; \ while :; do \ if wget -q -T 60 "$url" -O "${destination}.part"; then mv "${destination}.part" "$destination"; return 0; fi; \ rm -f "${destination}.part"; \ [ "$attempt" -lt 5 ] || return 1; \ sleep "$attempt"; attempt=$((attempt + 1)); \ done; \ } \ && apk add --no-cache p7zip \ && if [ -f "/tmp/emulatorjs-input/${EMULATORJS_VERSION}.7z" ]; then cp "/tmp/emulatorjs-input/${EMULATORJS_VERSION}.7z" /tmp/emulatorjs.7z; else fetch "https://github.com/EmulatorJS/EmulatorJS/releases/download/v${EMULATORJS_VERSION}/${EMULATORJS_VERSION}.7z" /tmp/emulatorjs.7z; fi \ && echo "${EMULATORJS_SHA256} /tmp/emulatorjs.7z" | sha256sum -c - \ && 7z x -y /tmp/emulatorjs.7z -o/emulatorjs >/dev/null \ && find /emulatorjs/data/cores -type f ! -path '/emulatorjs/data/cores/reports/*' ! -name 'fceumm-*' ! -name 'snes9x-*' ! -name 'gambatte-*' ! -name 'mgba-*' ! -name 'melonds-*' ! -name 'genesis_plus_gx-*' ! -name 'pcsx_rearmed-*' ! -name 'mednafen_psx_hw-*' ! -name 'ppsspp-*' ! -name 'cores.json' ! -name 'core-README.md' -delete \ && sed -i 's#https://cdn.emulatorjs.org/stable/data/version.json#/emulatorjs/data/version.json#g' /emulatorjs/data/emulator.min.js /emulatorjs/data/src/emulator.js \ && ! grep -R -F 'https://cdn.emulatorjs.org/stable/data/version.json' /emulatorjs/data \ && printf '%s\n' 'EmulatorJS 4.2.3 update metadata is resolved from the bundled same-origin version.json; no runtime CDN request is allowed.' > /emulatorjs/LUDARIUM_PATCHES.txt \ && rm /tmp/emulatorjs.7z FROM alpine:3.23@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40 AS playjs ARG PLAYJS_UPSTREAM_COMMIT=04bde0df87ee7c0e2f0151b51bb2cc22c88541da ARG PLAYJS_JS_SHA256=5599ff16ae5e3534f9b779736aedfa20661821d57be7f03e587e31ef8d090317 ARG PLAYJS_WASM_SHA256=de3ae0efdd7938d1b5ac754a096ec180094d64702ae84b962c4bb9f79abb1da7 ARG PLAYJS_LICENSE_SHA256=e9089da5950600267bb657503aba6eb59cb679b455c2997d341d83144144daab RUN fetch() { \ url="$1"; destination="$2"; attempt=1; \ while :; do \ if wget -q -T 60 "$url" -O "${destination}.part"; then mv "${destination}.part" "$destination"; return 0; fi; \ rm -f "${destination}.part"; \ [ "$attempt" -lt 5 ] || return 1; \ sleep "$attempt"; attempt=$((attempt + 1)); \ done; \ } \ && mkdir -p /playjs \ && fetch https://playjs.purei.org/Play.js /playjs/Play.js \ && fetch https://playjs.purei.org/Play.wasm /playjs/Play.wasm \ && fetch "https://raw.githubusercontent.com/jpd002/Play-/${PLAYJS_UPSTREAM_COMMIT}/License.txt" /playjs/LICENSE.txt \ && echo "${PLAYJS_JS_SHA256} /playjs/Play.js" | sha256sum -c - \ && echo "${PLAYJS_WASM_SHA256} /playjs/Play.wasm" | sha256sum -c - \ && echo "${PLAYJS_LICENSE_SHA256} /playjs/LICENSE.txt" | sha256sum -c - \ && printf '%s\n' \ "source=https://github.com/jpd002/Play-" \ "commit=${PLAYJS_UPSTREAM_COMMIT}" \ "Play.js.sha256=${PLAYJS_JS_SHA256}" \ "Play.wasm.sha256=${PLAYJS_WASM_SHA256}" \ "License.txt.sha256=${PLAYJS_LICENSE_SHA256}" > /playjs/PROVENANCE.txt FROM alpine:3.23@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40 AS n64wasm ARG N64WASM_COMMIT=bfac222f8a27287022844b47000328531834e9c1 ARG N64WASM_JS_SHA256=1db1dce5e356d1a1673d5dab6f73afd30bf4b69523c3292b1d44e33ff8f4d22e ARG N64WASM_WASM_SHA256=c829ac2ac2a620816a3906af1135c1203f606b92ae7a6e118ea8a90749f028ea ARG N64WASM_ASSETS_SHA256=72f8b414469d0b40630c82ca193b6f67dba906bd84ccc43991ee1ac3e4b900a2 ARG N64WASM_LICENSE_SHA256=46f8e130794f92e25e5865be3a363c3fb30210a428303c27262690cfc375c3d4 RUN fetch() { \ url="$1"; destination="$2"; attempt=1; \ while :; do \ if wget -q -T 60 "$url" -O "${destination}.part"; then mv "${destination}.part" "$destination"; return 0; fi; \ rm -f "${destination}.part"; \ [ "$attempt" -lt 5 ] || return 1; \ sleep "$attempt"; attempt=$((attempt + 1)); \ done; \ } \ && mkdir -p /n64wasm \ && fetch "https://raw.githubusercontent.com/nbarkhina/N64Wasm/${N64WASM_COMMIT}/dist/n64wasm.js" /n64wasm/n64wasm.js \ && fetch "https://raw.githubusercontent.com/nbarkhina/N64Wasm/${N64WASM_COMMIT}/dist/n64wasm.wasm" /n64wasm/n64wasm.wasm \ && fetch "https://raw.githubusercontent.com/nbarkhina/N64Wasm/${N64WASM_COMMIT}/dist/assets.zip" /n64wasm/assets.zip \ && fetch "https://raw.githubusercontent.com/nbarkhina/N64Wasm/${N64WASM_COMMIT}/LICENSE" /n64wasm/LICENSE \ && echo "${N64WASM_JS_SHA256} /n64wasm/n64wasm.js" | sha256sum -c - \ && echo "${N64WASM_WASM_SHA256} /n64wasm/n64wasm.wasm" | sha256sum -c - \ && echo "${N64WASM_ASSETS_SHA256} /n64wasm/assets.zip" | sha256sum -c - \ && echo "${N64WASM_LICENSE_SHA256} /n64wasm/LICENSE" | sha256sum -c - FROM node:24.4.1-alpine@sha256:820e86612c21d0636580206d802a726f2595366e1b867e564cbc652024151e8a AS web WORKDIR /src COPY src/Ludarium.Web/package.json src/Ludarium.Web/package-lock.json ./ RUN npm ci --ignore-scripts COPY src/Ludarium.Web/ ./ RUN npm run build FROM mcr.microsoft.com/dotnet/sdk:10.0.302@sha256:72dd743782f2ae7e5476fd64f6a460045e3998dc862218b80e6944cba79a01b0 AS build WORKDIR /src COPY NuGet.config global.json Directory.Build.props VERSION Ludarium.slnx ./ COPY src/ ./src/ RUN dotnet restore src/Ludarium.Api/Ludarium.Api.csproj --locked-mode \ && dotnet restore src/Ludarium.HealthCheck/Ludarium.HealthCheck.csproj --locked-mode COPY --from=web /artifacts/web/ ./src/Ludarium.Api/wwwroot/ COPY --from=emulator /emulatorjs/ ./src/Ludarium.Api/wwwroot/emulatorjs/ COPY --from=playjs /playjs/ ./src/Ludarium.Api/wwwroot/playjs/ COPY --from=n64wasm /n64wasm/ ./src/Ludarium.Api/wwwroot/n64wasm/ RUN dotnet publish src/Ludarium.Api/Ludarium.Api.csproj -c Release --no-restore -o /app/publish /p:UseAppHost=false \ && dotnet publish src/Ludarium.HealthCheck/Ludarium.HealthCheck.csproj -c Release --no-restore -o /app/health /p:UseAppHost=false FROM postgres:16.15-bookworm@sha256:60f4761b9035e0b8d5218f701a8c3382f641bf12b1604822574cf5be3baeb537 AS postgres-entrypoint FROM mcr.microsoft.com/dotnet/aspnet:10.0.11-noble@sha256:a4556ed033fa96f984bb7a8d348851cb2d36b1281dd2420070045f664fbb5f94 AS final ARG DEBIAN_FRONTEND=noninteractive ARG POSTGRES_PACKAGE_VERSION=16.15-0ubuntu0.24.04.1 ARG GOSU_PACKAGE_VERSION=1.17-1ubuntu0.24.04.3 ARG LOCALES_PACKAGE_VERSION=2.39-0ubuntu8.8 ARG LUDARIUM_SOURCE_REVISION=local ARG LUDARIUM_RELEASE_VERSION=unversioned LABEL org.opencontainers.image.revision="${LUDARIUM_SOURCE_REVISION}" LABEL org.opencontainers.image.version="${LUDARIUM_RELEASE_VERSION}" RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ "postgresql-16=${POSTGRES_PACKAGE_VERSION}" \ "gosu=${GOSU_PACKAGE_VERSION}" \ "locales=${LOCALES_PACKAGE_VERSION}" \ && sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ && locale-gen en_US.UTF-8 \ && locale -a | grep -Fx 'en_US.utf8' \ && rm -rf /var/lib/apt/lists/* /var/lib/postgresql/16/main WORKDIR /app ENV ASPNETCORE_URLS=http://0.0.0.0:8734 \ DOTNET_ROOT=/usr/share/dotnet \ DOTNET_EnableDiagnostics=0 \ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \ LANG=C.UTF-8 \ LC_ALL=C.UTF-8 \ LUDARIUM_EXPORTS=/app/exports \ PG_MAJOR=16 \ PGDATA=/var/lib/postgresql/data \ PATH=/usr/lib/postgresql/16/bin:$PATH \ POSTGRES_DB=ludarium \ POSTGRES_USER=ludarium COPY --from=postgres-entrypoint /usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh COPY --from=postgres-entrypoint /usr/local/bin/docker-ensure-initdb.sh /usr/local/bin/docker-ensure-initdb.sh COPY --from=build --chown=1654:1654 /app/publish ./ COPY --from=build --chown=1654:1654 /app/health ./health/ COPY --chmod=755 deploy/ludarium-entrypoint.sh /usr/local/bin/ludarium-entrypoint.sh COPY --chmod=755 deploy/repair-postgres-collation.sh /usr/local/bin/repair-postgres-collation.sh RUN ln -s docker-ensure-initdb.sh /usr/local/bin/docker-enforce-initdb.sh \ && mkdir -p "$PGDATA" /docker-entrypoint-initdb.d \ && chown -R postgres:postgres /var/lib/postgresql \ && sed -i 's/\r$//' /usr/local/bin/ludarium-entrypoint.sh EXPOSE 8734 STOPSIGNAL SIGTERM HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 CMD ["/usr/share/dotnet/dotnet", "/app/health/Ludarium.HealthCheck.dll"] ENTRYPOINT ["/usr/local/bin/ludarium-entrypoint.sh"]