Three CI wins from ersatztv#190, all config-only: - Cache ~/.nuget/packages via actions/cache@v4 in both the `test` and `migrations` jobs, keyed on Directory.Packages.props + global.json, so dotnet restore no longer starts from scratch every run. - Shallow-checkout the `test` job (fetch-depth: 1) since it never runs git describe/log; only `build` needs full history for versioning. The `migrations` job's checkout was already effectively shallow (default fetch-depth 1), documented explicitly. - Reorder the Dockerfile so the openapi-generator jar wget happens before the openapi spec COPY, decoupling the ~30MB download layer from spec changes (e.g. v1.json) that previously busted it on every API PR. Documented all three in docs/ci-cd.md alongside the existing job/Dockerfile notes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
90 lines
4.2 KiB
Docker
90 lines
4.2 KiB
Docker
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-amd64 AS dotnet-runtime
|
|
|
|
FROM node:22-bookworm-slim AS web-build
|
|
WORKDIR /source
|
|
COPY web/package*.json ./web/
|
|
WORKDIR /source/web
|
|
RUN npm ci
|
|
WORKDIR /source
|
|
COPY design-system/. ./design-system/
|
|
COPY web/. ./web/
|
|
WORKDIR /source/web
|
|
RUN npm run lint && npm run typecheck && npm test -- --run && npm run build
|
|
|
|
FROM --platform=linux/amd64 192.168.1.95:3000/timothy/ersatztv-ffmpeg:8.1.2 AS runtime-base
|
|
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
|
|
RUN apt-get update && \
|
|
apt-get install -y --no-install-recommends python3 python3-pip && \
|
|
python3 -m pip install --target=/app/pythonlibs --no-cache-dir streamlink && \
|
|
apt-get clean -y && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# https://hub.docker.com/_/microsoft-dotnet
|
|
FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 AS build
|
|
RUN apt-get update && apt-get install -y ca-certificates gnupg default-jre-headless python3-pip
|
|
WORKDIR /source
|
|
|
|
# download the openapi-generator jar first so this layer is cached independently
|
|
# of the openapi spec dir below -- otherwise every spec change (e.g. v1.json)
|
|
# busts the layer and re-downloads the ~30MB jar (ersatztv#190)
|
|
RUN wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar
|
|
|
|
# generate openapi client
|
|
COPY ErsatzTV/wwwroot/openapi/. /app/ErsatzTV/wwwroot/openapi/
|
|
RUN java -jar openapi-generator-cli-7.15.0.jar generate -i /app/ErsatzTV/wwwroot/openapi/scripted-schedule.json -g python -o /app/etv-client --package-name etv_client
|
|
RUN rm -rf openapi-generator-cli-7.15.0.jar /app/ErsatzTV
|
|
RUN python3 -m pip install --target=/app/pythonlibs /app/etv-client
|
|
RUN rm -rf /app/etv-client
|
|
COPY scripts/scripted-schedules/. /app/scripted-schedules/
|
|
|
|
# copy csproj and restore as distinct layers
|
|
COPY *.sln .
|
|
# repo-wide build config (MSBuild props/targets incl. NuGet-audit warning
|
|
# exemptions, SDK pin, analyzer severities, and the Central Package Management
|
|
# version manifest) must be present before restore so the image build matches
|
|
# local/CI builds. Directory.Packages.props is REQUIRED here: with CPM the csproj
|
|
# carry no versions, so restore fails without the central manifest.
|
|
COPY Directory.Build.props Directory.Build.targets Directory.Packages.props global.json .editorconfig ./
|
|
COPY artwork/* ./artwork/
|
|
COPY ErsatzTV/*.csproj ./ErsatzTV/
|
|
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
|
|
COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/
|
|
COPY ErsatzTV.Core.Nullable/*.csproj ./ErsatzTV.Core.Nullable/
|
|
COPY ErsatzTV.FFmpeg/*.csproj ./ErsatzTV.FFmpeg/
|
|
COPY ErsatzTV.Infrastructure/*.csproj ./ErsatzTV.Infrastructure/
|
|
COPY ErsatzTV.Infrastructure.Sqlite/*.csproj ./ErsatzTV.Infrastructure.Sqlite/
|
|
COPY ErsatzTV.Infrastructure.MySql/*.csproj ./ErsatzTV.Infrastructure.MySql/
|
|
COPY ErsatzTV.Scanner/*.csproj ./ErsatzTV.Scanner/
|
|
RUN dotnet restore -r linux-x64 ErsatzTV/ErsatzTV.csproj
|
|
|
|
# copy everything else and build app
|
|
COPY ErsatzTV/. ./ErsatzTV/
|
|
COPY --from=web-build /source/ErsatzTV/wwwroot/app/. ./ErsatzTV/wwwroot/app/
|
|
COPY ErsatzTV.Application/. ./ErsatzTV.Application/
|
|
COPY ErsatzTV.Core/. ./ErsatzTV.Core/
|
|
COPY ErsatzTV.Core.Nullable/. ./ErsatzTV.Core.Nullable/
|
|
COPY ErsatzTV.FFmpeg/. ./ErsatzTV.FFmpeg/
|
|
COPY ErsatzTV.Infrastructure/. ./ErsatzTV.Infrastructure/
|
|
COPY ErsatzTV.Infrastructure.Sqlite/. ./ErsatzTV.Infrastructure.Sqlite/
|
|
COPY ErsatzTV.Infrastructure.MySql/. ./ErsatzTV.Infrastructure.MySql/
|
|
COPY ErsatzTV.Scanner/. ./ErsatzTV.Scanner/
|
|
ARG INFO_VERSION="unknown"
|
|
ARG BUILD_CONFIG="release"
|
|
WORKDIR /source/ErsatzTV.Scanner
|
|
RUN dotnet publish ErsatzTV.Scanner.csproj -c ${BUILD_CONFIG} -o /app -r linux-x64 --self-contained false --no-restore /p:DebugType=Embedded /p:InformationalVersion=${INFO_VERSION}
|
|
WORKDIR /source/ErsatzTV
|
|
RUN sed -i '/Scanner/d' ErsatzTV.csproj
|
|
RUN dotnet publish ErsatzTV.csproj -c ${BUILD_CONFIG} -o /app -r linux-x64 --self-contained false --no-restore /p:DebugType=Embedded /p:InformationalVersion=${INFO_VERSION}
|
|
|
|
# final stage/image
|
|
FROM runtime-base
|
|
ENV FONTCONFIG_PATH=/etc/fonts
|
|
RUN fc-cache update
|
|
WORKDIR /app
|
|
COPY --from=build /app ./
|
|
ENV PYTHONPATH=/app/pythonlibs
|
|
ENV ETV_CONFIG_FOLDER=/config
|
|
ENV ETV_TRANSCODE_FOLDER=/transcode
|
|
ENV ETV_DISABLE_VULKAN=1
|
|
ENTRYPOINT ["./ErsatzTV"]
|