Files
Felitendo 2c0356317b fix(admin): let a boot apply the changes a push refuses to make
`prisma db push` is how both containers reconcile the database at start, and
it stops the moment a change could destroy data — a dropped column, a dropped
table, a unique index over rows it has not seen. It stops for the whole push,
so the reaction table and the `upvotes` column left a deployment restart-
looping on "Use the --accept-data-loss flag", with the identity columns and
the download token stuck behind them.

`prisma/pre-push.sql` is every one of those changes written out in full and
applied first, so the push that follows only ever sees work it considers safe.
It is idempotent, a no-op on a fresh database, and it carries the whole series
since local IPA storage, so a deployment that skipped four releases catches up
in one boot. Both containers run it; an advisory lock makes the second one
wait rather than race the first.

Not `--accept-data-loss`: that flag would wave through the next drop nobody
meant to make. A destructive change stays something somebody wrote down.
2026-08-07 02:50:45 +02:00

112 lines
4.5 KiB
Docker

# NOTE: this image builds from the **repository root**, not from `web/`.
# The landing page's install endpoint signs with zsign, and zsign is built here
# from the monorepo's own `manager/Zsign` sources, so the server signs with
# exactly the code the Manager signs with on-device. `Dockerfile.worker` still
# builds from `web/` — the worker never signs anything.
#
# Stage 0: zsign
#
# bookworm-slim on purpose: node:20-slim is bookworm too, so the binary links
# against the same libssl3/libminizip1 the runner installs.
FROM debian:bookworm-slim AS zsign
# zlib1g-dev is not redundant: minizip's own headers open with `#include
# "zlib.h"` and libminizip-dev does not pull the zlib headers in behind it.
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ make pkg-config libssl-dev libminizip-dev zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src
COPY manager/Zsign ./Zsign
# Upstream maintains the Swift package, not this CLI. `ZBundle::SignFolder`
# grew a tenth parameter, `src/zsign.mm` was updated for it and `src/zsign.cpp`
# was not, so `build/linux` does not compile at the commit the submodule is
# pinned to. The argument is supplied here rather than by moving the pin:
# ios/Zsign and manager/Zsign sit at the same commit, and moving it would change
# what both apps sign with on-device to fix a server build.
#
# The parameter reads backwards — `true` is what *writes* embedded.mobileprovision
# and seals it into the signature, `false` strips it. `true` is therefore the
# value to pass: it is what the apps send by default (ZsignHandler passes
# `removeProvision: !options.removeProvisioning`), and an over-the-air install
# is refused without the profile.
#
# The grep is a tripwire, not decoration. If a later submodule bump fixes the
# call site, this stops the build instead of letting a stale edit through.
RUN grep -q 'bWeakInject, bEnableCache);' Zsign/src/zsign.cpp && \
sed -i 's/bWeakInject, bEnableCache);/bWeakInject, bEnableCache, true);/' \
Zsign/src/zsign.cpp
# The Makefile writes to ../../bin/zsign, relative to build/linux.
RUN make -C Zsign/build/linux -j"$(nproc)"
# Stage 1: Dependencies
FROM node:20-slim AS deps
WORKDIR /app
COPY web/package.json web/package-lock.json ./
RUN npm ci --ignore-scripts
COPY web/prisma ./prisma/
RUN npx prisma generate
# Stage 2: Build
FROM node:20-slim AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY web/ .
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN npm run build
# Stage 3: Runner
FROM node:20-slim AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV ZSIGN_PATH=/usr/local/bin/zsign
# libminizip1 is zsign's only runtime dependency beyond libssl3, which the
# openssl package already pulls in.
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
libminizip1 \
&& rm -rf /var/lib/apt/lists/* && \
addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
COPY --from=zsign /src/Zsign/bin/zsign /usr/local/bin/zsign
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/scripts ./scripts
# /app/storage holds the IPA files and tweak icons and is shared with the
# worker. NOTE: a bind mount there shadows this directory, so the host path must
# already be owned by uid/gid 1001 — the build-time chown does not reach it.
#
# storage/install is the landing page's signing scratch: signed builds waiting
# for an `itms-services://` prompt to collect them, swept on a short expiry.
RUN mkdir -p /app/tdlib-data/db /app/tdlib-data/files \
/app/storage/ipa /app/storage/icons /app/storage/tmp \
/app/storage/install \
/tmp/ftstore && \
chown -R nextjs:nodejs /app /tmp/ftstore
USER nextjs
EXPOSE 3000
# `pre-push.sql` first, every boot: it applies the changes `db push` refuses to
# make to a database that already has rows — a dropped column, a dropped table,
# a unique index over existing rows — so the push that follows only ever sees
# work it considers safe. It is a no-op on a fresh database and on one already
# up to date. See `prisma/pre-push.sql` for why this is not `--accept-data-loss`.
CMD ["sh", "-c", "node node_modules/prisma/build/index.js db execute --schema prisma/schema.prisma --file prisma/pre-push.sql && node node_modules/prisma/build/index.js db push --skip-generate && node server.js"]