`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.
112 lines
4.5 KiB
Docker
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"]
|