-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContainerfile
More file actions
249 lines (203 loc) · 11.3 KB
/
Copy pathContainerfile
File metadata and controls
249 lines (203 loc) · 11.3 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# --------------------------------------------------------------------------
# THIS FILE IS AUTOGENERATED - DO NOT EDIT MANUALLY
#
# Source: Containerfile.j2
# --------------------------------------------------------------------------
# Global ARGs must be declared before the first FROM to be usable in FROM lines
ARG BASE_VERSION=15
ARG NODE_VERSION=22
# Stage 1: Pull upstream AFFiNE Linux image to extract pre-built frontend + server JS
# (These are platform-agnostic; only the native binaries differ per OS)
FROM --platform=linux/amd64 ghcr.io/toeverything/affine:stable AS upstream
# Stage 2: Build FreeBSD production Node.js dependencies
FROM ghcr.io/daemonless/base:${BASE_VERSION} AS deps-builder
ARG NODE_VERSION
RUN pkg update && \
pkg install -y \
node${NODE_VERSION} npm-node${NODE_VERSION} \
FreeBSD-clang FreeBSD-clibs-dev FreeBSD-toolchain \
git ca_root_nss jq && \
pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
# Enable yarn 4 via corepack — version is determined by the project's packageManager field
RUN corepack enable
# Clone AFFiNE at the latest stable release
ARG VERSION=latest
RUN if [ "$VERSION" = "latest" ]; then \
VERSION=$(fetch -qo - "https://api.github.com/repos/toeverything/AFFiNE/releases/latest" | jq -r .tag_name); \
fi && \
echo "${VERSION}" > /version.txt && \
git clone --depth 1 --branch "${VERSION}" https://github.com/toeverything/AFFiNE.git /src
WORKDIR /src
# Install production dependencies.
# --ignore-scripts skips postinstall (avoids Prisma engine download for unsupported FreeBSD target).
# Remove the build script so yarn doesn't try to compile TypeScript during install.
# We use the pre-built dist from the upstream Linux image instead.
RUN node -e " \
const fs = require('fs'); \
const pkg = JSON.parse(fs.readFileSync('packages/backend/server/package.json')); \
delete pkg.scripts.build; delete pkg.scripts.postinstall; \
fs.writeFileSync('packages/backend/server/package.json', JSON.stringify(pkg, null, 2));"
RUN HUSKY=0 ELECTRON_SKIP_BINARY_DOWNLOAD=1 SENTRYCLI_SKIP_DOWNLOAD=1 \
yarn workspaces focus @affine/server --production
# Save Prisma engine version so native-builder can clone the matching prisma-engines commit.
# The upstream image already has a pre-generated .prisma/client/ (pure JS, platform-agnostic)
# that we copy directly in the runtime stage — no need to run prisma generate on FreeBSD.
RUN node -e "console.log(require('./node_modules/@prisma/engines-version').enginesVersion)" \
> /engine-version.txt
# Move node_modules into the server package directory (matches official image layout)
RUN mv node_modules packages/backend/server/
# Stage 3: Build FreeBSD native binaries
# - server-native: AFFiNE's Rust NAPI module (doc processing, AI, image handling, etc.)
# - prisma query-engine-node-api: Prisma's Node.js query engine for FreeBSD
# - prisma schema-engine-cli: used by predeploy script for database migrations
FROM ghcr.io/daemonless/base:${BASE_VERSION} AS native-builder
ARG NODE_VERSION
RUN pkg update && \
pkg install -y \
node${NODE_VERSION} \
FreeBSD-clang FreeBSD-clang-dev FreeBSD-clibs-dev FreeBSD-openssl-dev \
FreeBSD-toolchain FreeBSD-libexecinfo-dev FreeBSD-runtime-dev \
FreeBSD-utilities-dev FreeBSD-zlib-dev \
cmake gmake rust git python3 ca_root_nss && \
pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
# node22 pkg on FreeBSD installs as /usr/local/bin/node directly (no node22 symlink needed)
COPY --from=deps-builder /version.txt /version.txt
COPY --from=deps-builder /engine-version.txt /engine-version.txt
# Patch script to fix metrics-0.23.0 Rust 1.94.0 E0521 lifetime error
COPY patch-metrics.py /patch-metrics.py
# Clone AFFiNE at the same version for the Cargo workspace
RUN git clone --depth 1 --branch "$(cat /version.txt)" \
https://github.com/toeverything/AFFiNE.git /src
WORKDIR /src
# Remove rust-toolchain.toml so cargo uses the system Rust package (avoids rustup requirement).
# FreeBSD pkg Rust is >= 1.94.0 which satisfies AFFiNE's toolchain requirement.
RUN rm -f rust-toolchain.toml
# Build server-native NAPI module for FreeBSD.
# global-dynamic TLS (CFLAGS + RUSTFLAGS relocation-model=pic) lets the .node
# file be dlopen'd by Node.js without the "No space for static TLS" error.
RUN CFLAGS="-ftls-model=global-dynamic" \
RUSTFLAGS="-C relocation-model=pic" \
cargo build --release -p affine_server_native 2>&1 | tail -20 && \
cp target/release/libaffine_server_native.so /server-native.node && \
rm -rf target ~/.cargo/registry ~/.cargo/git
# Build Prisma engines for FreeBSD from the pinned commit matching the installed Prisma version.
# metrics-0.23.0 (a transitive dep of prisma-engines) fails to compile on Rust >=1.94.0
# due to E0521 (lifetime escape via raw pointer coercion of trait objects).
# Fix: cargo fetch to populate the cache, then patch metrics-0.23.0 to add 'static
# bound on set_local_recorder's type parameter before building.
RUN ENGINE_VER=$(cat /engine-version.txt) && \
git clone https://github.com/prisma/prisma-engines.git /prisma-engines && \
cd /prisma-engines && \
git checkout "${ENGINE_VER}" && \
cargo fetch 2>&1 | tail -5 && \
python3 /patch-metrics.py && \
cargo build --release -p query-engine-node-api -p schema-engine-cli 2>&1 | tail -20 && \
cp target/release/libquery_engine.so /libquery_engine.so.node && \
cp target/release/schema-engine /schema-engine && \
chmod 755 /schema-engine && \
rm -rf /prisma-engines ~/.cargo/registry ~/.cargo/git
# Build @node-rs/crc32 and @node-rs/argon2 from source.
# Pre-built FreeBSD binaries use initial-exec TLS which fails when dlopen'd by Node.js.
# Building natively on FreeBSD uses the correct TLS model.
RUN git clone --depth 1 https://github.com/napi-rs/node-rs.git /node-rs && \
cd /node-rs && \
CMAKE_MAKE_PROGRAM=/usr/local/bin/gmake \
RUSTFLAGS="-C relocation-model=pic" \
cargo build --release -p node-rs-crc32 -p node-rs_argon2 2>&1 | tail -10 && \
cp target/release/libnode_rs_crc32.so /crc32.freebsd-x64.node && \
cp target/release/libnode_rs_argon2.so /argon2.freebsd-x64.node && \
rm -rf /node-rs ~/.cargo/registry ~/.cargo/git
# Production image
FROM ghcr.io/daemonless/base:${BASE_VERSION}
ARG FREEBSD_ARCH=amd64
ARG NODE_VERSION
ARG PACKAGES="node${NODE_VERSION} openssl ca_root_nss"
ARG UPSTREAM_URL="https://api.github.com/repos/toeverything/AFFiNE/releases/latest"
ARG UPSTREAM_JQ=".tag_name"
ARG HEALTHCHECK_ENDPOINT="http://localhost:3010/api/info"
ENV HEALTHCHECK_URL="${HEALTHCHECK_ENDPOINT}"
# --- Metadata (Injected by Generator) ---
LABEL org.opencontainers.image.title="AFFiNE" \
org.opencontainers.image.description="AFFiNE is an open-source, privacy-first, local-first knowledge management and collaboration tool." \
org.opencontainers.image.source="https://github.com/daemonless/affine" \
org.opencontainers.image.url="https://affine.pro/" \
org.opencontainers.image.documentation="https://docs.affine.pro/self-hosting/faq" \
org.opencontainers.image.licenses="MIT AND LicenseRef-AFFiNE-EE" \
org.opencontainers.image.vendor="daemonless" \
org.opencontainers.image.authors="daemonless" \
io.daemonless.category="Productivity" \
io.daemonless.volumes="/config" \
io.daemonless.arch="${FREEBSD_ARCH}" \
io.daemonless.upstream-url="${UPSTREAM_URL}" \
io.daemonless.upstream-jq="${UPSTREAM_JQ}" \
io.daemonless.healthcheck-url="${HEALTHCHECK_ENDPOINT}" \
io.daemonless.packages="${PACKAGES}"
# Install runtime dependencies
RUN pkg update && \
pkg install -y ${PACKAGES} && \
pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
# Enable yarn via corepack — needed by the predeploy migration script
ENV COREPACK_HOME=/usr/local/share/corepack
RUN corepack enable
# node22 pkg installs as /usr/local/bin/node — no symlink needed
# Copy pre-built server artifacts from the official Linux image.
# dist/ and static/ are pure JavaScript — platform-agnostic.
COPY --from=upstream /app/dist /app/dist
COPY --from=upstream /app/static /app/static
COPY --from=upstream /app/scripts /app/scripts
COPY --from=upstream /app/migrations /app/migrations
COPY --from=upstream /app/schema.prisma /app/schema.prisma
COPY --from=upstream /app/package.json /app/package.json
# Pre-install the yarn version declared in the monorepo root package.json
# (the server package.json lacks packageManager; the root has it).
# Make the cache world-readable so the bsd user can use it without writing.
COPY --from=deps-builder /src/package.json /tmp/root-package.json
RUN cd /tmp && mv root-package.json package.json && corepack install && \
chown -R bsd:bsd /usr/local/share/corepack && rm -f package.json
# Copy FreeBSD production node_modules
COPY --from=deps-builder /src/packages/backend/server/node_modules /app/node_modules
COPY --from=deps-builder /version.txt /app/version
# Copy pre-generated Prisma client JS from upstream (platform-agnostic, avoids prisma generate on FreeBSD)
COPY --from=upstream /app/node_modules/.prisma /app/node_modules/.prisma
# Install FreeBSD server-native NAPI module.
# AFFiNE bundles the native binary into dist/ and loads it as ./server-native.x64.node.
# Replace the Linux binary from the upstream image with our FreeBSD build.
COPY --from=native-builder /server-native.node /app/dist/server-native.x64.node
# Install FreeBSD @node-rs native binaries (pre-built binaries use initial-exec TLS, broken on FreeBSD)
COPY --from=native-builder /crc32.freebsd-x64.node \
/app/node_modules/@node-rs/crc32-freebsd-x64/crc32.freebsd-x64.node
COPY --from=native-builder /argon2.freebsd-x64.node \
/app/node_modules/@node-rs/argon2-freebsd-x64/argon2.freebsd-x64.node
# Install FreeBSD Prisma query engine (replaces Linux binary downloaded during generate)
RUN rm -f /app/node_modules/.prisma/client/libquery_engine-*.so.node \
/app/node_modules/prisma/libquery_engine-*.so.node 2>/dev/null || true
COPY --from=native-builder /libquery_engine.so.node \
/app/node_modules/.prisma/client/libquery_engine.so.node
# Install FreeBSD Prisma schema engine (for predeploy migrations)
RUN mkdir -p /app/node_modules/@prisma/engines && \
rm -f /app/node_modules/@prisma/engines/schema-engine-* \
/app/node_modules/@prisma/engines/libquery_engine-*.so.node 2>/dev/null || true
COPY --from=native-builder /schema-engine \
/app/node_modules/@prisma/engines/schema-engine
RUN chmod 755 /app/node_modules/@prisma/engines/schema-engine
# Ensure app files are readable by bsd user (UID 1000)
RUN chmod -R o+rX /app
# NestJS GraphQL generates schema to /app/src/ at startup — must be writable by bsd
RUN mkdir -p /app/src && chown bsd:bsd /app/src
# Create config/data directory (HOME=/config → AFFiNE uses /config/.affine/)
RUN mkdir -p /config && chown -R bsd:bsd /config
# Prisma engine paths — bypass platform detection, use our FreeBSD binaries
ENV PRISMA_QUERY_ENGINE_LIBRARY=/app/node_modules/.prisma/client/libquery_engine.so.node
ENV PRISMA_SCHEMA_ENGINE_BINARY=/app/node_modules/@prisma/engines/schema-engine
# Application environment
ENV NODE_ENV=production \
PORT=3010 \
HOME=/config \
AFFINE_INDEXER_ENABLED=false
# Copy service definitions and init scripts
COPY root/ /
RUN chmod +x /etc/services.d/affine/run \
/etc/cont-init.d/* 2>/dev/null || true
# --- Expose (Injected by Generator) ---
# --- Volumes (Injected by Generator) ---
VOLUME /config