Repository navigation
Expand file tree
/
Copy pathDockerfile.worker
More file actions
115 lines (91 loc) · 4.57 KB
/
Copy pathDockerfile.worker
File metadata and controls
115 lines (91 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# syntax=docker/dockerfile:1
# check=error=true;skip=SecretsUsedInArgOrEnv
# Worker image for Solid Queue job processing. Processes all queues including
# `heavy` (image/video reprocessing), so it needs ffmpeg, imagemagick, and libvips.
ARG RUBY_VERSION=3.4.11
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
WORKDIR /rails
# Pin media/system tools to current Debian 12 (bookworm) stable versions. Pins may need
# updating when Debian rolls a point release — failing the build is preferable to
# silently pulling in a new version that hasn't been reviewed.
ARG CURL_VERSION=7.88.1-10+deb12u15
ARG LIBJEMALLOC2_VERSION=5.3.0-1
ARG LIBVIPS42_VERSION=8.14.1-3+deb12u3
ARG SQLITE3_VERSION=3.40.1-2+deb12u2
ARG FFMPEG_VERSION=7:5.1.9-0+deb12u1
ARG IMAGEMAGICK_VERSION=8:6.9.11.60+dfsg-1.6+deb12u13
# pdftoppm, used by ShipChecks::PdfRasterizer to render zine PDFs in a separate
# process. libvips's own pdfload stays blocked (CVE-2026-66066); poppler is only
# ever reached through that child process, never in-process.
ARG POPPLER_UTILS_VERSION=22.12.0-2+deb12u3
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y \
curl="${CURL_VERSION}" \
libjemalloc2="${LIBJEMALLOC2_VERSION}" \
libvips42="${LIBVIPS42_VERSION}" \
sqlite3="${SQLITE3_VERSION}" \
ffmpeg="${FFMPEG_VERSION}" \
imagemagick="${IMAGEMAGICK_VERSION}" \
poppler-utils="${POPPLER_UTILS_VERSION}" \
python3 \
python3-pip && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Harden ImageMagick: disable coders/paths used for ImageTragick-class RCE.
COPY config/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
# Deno for yt-dlp's JS challenge solver (n-challenge). Without a JS runtime, yt-dlp can't solve
# YouTube's n-challenge and only gets storyboard-only format lists. Deno is yt-dlp's best-tested
# EJS runtime; node is NOT detected by the current yt-dlp release. Ships as a standalone binary
# (a zip, extracted with the system python3 since the image has no unzip).
ARG DENO_VERSION=v2.8.3
RUN set -eux; \
curl -fsSL "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-x86_64-unknown-linux-gnu.zip" -o /tmp/deno.zip; \
python3 -c "import zipfile; zipfile.ZipFile('/tmp/deno.zip').extractall('/usr/local/bin')"; \
chmod 0755 /usr/local/bin/deno; \
rm /tmp/deno.zip; \
deno --version
# yt-dlp installed as a Python package (not the linux_exe binary) so it uses the system Python
# and can detect the deno JS runtime for the n-challenge solver.
# DELIBERATELY not pinned: YouTube changes extraction constantly, stale yt-dlp breaks downloads.
ARG YTDLP_VERSION=latest
RUN pip3 install --break-system-packages \
$([ "$YTDLP_VERSION" = "latest" ] && echo "yt-dlp[default]" || echo "yt-dlp[default]==${YTDLP_VERSION}") && \
yt-dlp --version
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
FROM base AS build
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential curl git libyaml-dev pkg-config && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
ARG NODE_VERSION=22
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && \
apt-get install --no-install-recommends -y nodejs && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
COPY Gemfile Gemfile.lock ./
# --jobs 1: parallel gem builds race on mkmf's global $stdout/$stderr reopen under
# the BuildKit builder, failing native extensions with Errno::EBADF. Serialize to avoid it.
RUN bundle install --jobs 1 && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN bundle exec bootsnap precompile app/ lib/
# Sentry release tag — pass with --build-arg SENTRY_RELEASE=$(git rev-parse HEAD)
ARG SENTRY_RELEASE=""
ENV SENTRY_RELEASE=${SENTRY_RELEASE}
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
FROM base AS app
# Re-declare and re-export so the Ruby Sentry SDK can read it at runtime
ARG SENTRY_RELEASE=""
ENV SENTRY_RELEASE=${SENTRY_RELEASE}
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash
# Ownership set at copy time to avoid a slow `chown -R` that would duplicate
# the entire tree into a new layer.
COPY --from=build --chown=rails:rails "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build --chown=rails:rails /rails /rails
USER 1000:1000
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
CMD ["./bin/jobs"]