-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Expand file tree
/
Copy pathDockerfile
More file actions
450 lines (431 loc) · 20.1 KB
/
Copy pathDockerfile
File metadata and controls
450 lines (431 loc) · 20.1 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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# debian:trixie (Debian 13), not ubuntu:26.04: Ubuntu publishes no linux/386
# image, which stopped the image from ever building for i386. Debian ships every
# arch this image targets - amd64, arm64, 386, arm/v7, ppc64le, riscv64, s390x -
# so ONE base covers them all, and it is the same base WeKan's per-arch .zip
# bundles are already built in (releases/... build-extra-arches).
#
# The ONE it does not ship is arm/v6: debian:trixie's manifest list has arm/v5
# and arm/v7 and nothing between them. That matters because containerd treats a
# LOWER ARM variant as compatible, so a linux/arm/v6 request does not fail - it
# quietly resolves to the arm/v5 (armel, SOFT-float) image, whose loader and
# glibc cannot run the hard-float node-armv6 in the armv6 bundle. The docker job
# therefore asks this base what it publishes and drops a platform it lacks; see
# docs/Platforms/FOSS/Container/Docker/CPU-platforms.md.
FROM debian:trixie
LABEL maintainer="wekan"
LABEL org.opencontainers.image.ref.name="debian"
LABEL org.opencontainers.image.version="trixie"
LABEL org.opencontainers.image.source="https://github.com/wekan/wekan"
# TARGETARCH and TARGETVARIANT are automatically provided by Docker Buildx
ARG TARGETARCH
ARG TARGETVARIANT
ARG VERSION=11.93
ARG DEBIAN_FRONTEND=noninteractive
ENV BUILD_DEPS="apt-utils gnupg wget bzip2 g++ curl libarchive-tools build-essential git ca-certificates python3 unzip"
ENV \
DEBUG=false \
DDP_TRANSPORT=sockjs \
NODE_VERSION=v26.10.0 \
METEOR_RELEASE=METEOR@3.6-beta.1 \
USE_EDGE=false \
NPM_VERSION=12.1.0 \
SRC_PATH=./ \
WITH_API=true \
MONGO_OPLOG_URL="" \
RESULTS_PER_PAGE="" \
DEFAULT_BOARD_ID="" \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90 \
ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="" \
ATTACHMENTS_UPLOAD_MIME_TYPES="" \
ATTACHMENTS_UPLOAD_MAX_SIZE=0 \
AVATARS_UPLOAD_EXTERNAL_PROGRAM="" \
AVATARS_UPLOAD_MIME_TYPES="" \
AVATARS_UPLOAD_MAX_SIZE=72000 \
CARD_OPENED_WEBHOOK_ENABLED=false \
MAX_IMAGE_PIXEL="" \
IMAGE_COMPRESS_RATIO="" \
NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE="" \
BIGEVENTS_PATTERN=NONE \
NOTIFY_ON_ASSIGN="true" \
NOTIFY_DUE_DAYS_BEFORE_AND_AFTER="" \
NOTIFY_DUE_AT_HOUR_OF_DAY="" \
EMAIL_NOTIFICATION_TIMEOUT=30000 \
MATOMO_ADDRESS="" \
MATOMO_SITE_ID="" \
MATOMO_DO_NOT_TRACK=true \
MATOMO_WITH_USERNAME=false \
METRICS_ALLOWED_IP_ADDRESSES="" \
BROWSER_POLICY_ENABLED=true \
TRUSTED_URL="" \
WEBHOOKS_ATTRIBUTES="" \
OAUTH2_ENABLED=false \
OIDC_REDIRECTION_ENABLED=false \
OAUTH2_CA_CERT="" \
OAUTH2_ADFS_ENABLED=false \
OAUTH2_B2C_ENABLED=false \
OAUTH2_LOGIN_STYLE=redirect \
OAUTH2_CLIENT_ID="" \
OAUTH2_SECRET="" \
OAUTH2_SECRET_FILE="" \
OAUTH2_SERVER_URL="" \
OAUTH2_AUTH_ENDPOINT="" \
OAUTH2_USERINFO_ENDPOINT="" \
OAUTH2_TOKEN_ENDPOINT="" \
OAUTH2_LOGOUT_ENDPOINT="" \
OAUTH2_ID_MAP="" \
OAUTH2_USERNAME_MAP="" \
OAUTH2_AUTO_REGISTRATION="true" \
OAUTH2_ADMIN_GROUPS="" \
OAUTH2_FULLNAME_MAP="" \
OAUTH2_ID_TOKEN_WHITELIST_FIELDS="" \
OAUTH2_REQUEST_PERMISSIONS='openid profile email' \
OAUTH2_EMAIL_MAP="" \
LDAP_ENABLE=false \
LDAP_PORT=389 \
LDAP_HOST="" \
LDAP_AD_SIMPLE_AUTH="" \
LDAP_USER_AUTHENTICATION=false \
LDAP_USER_AUTHENTICATION_FIELD=uid \
LDAP_BASEDN="" \
LDAP_LOGIN_FALLBACK=false \
LDAP_RECONNECT=true \
LDAP_TIMEOUT=10000 \
LDAP_IDLE_TIMEOUT=10000 \
LDAP_CONNECT_TIMEOUT=10000 \
LDAP_AUTHENTIFICATION=false \
LDAP_AUTHENTIFICATION_USERDN="" \
LDAP_AUTHENTIFICATION_PASSWORD="" \
LDAP_AUTHENTIFICATION_PASSWORD_FILE="" \
LDAP_LOG_ENABLED=false \
LDAP_BACKGROUND_SYNC=false \
LDAP_BACKGROUND_SYNC_INTERVAL="" \
LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false \
LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false \
LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS=false \
LDAP_ENCRYPTION=false \
LDAP_CA_CERT="" \
LDAP_REJECT_UNAUTHORIZED=false \
LDAP_USER_SEARCH_FILTER="" \
LDAP_USER_SEARCH_SCOPE="" \
LDAP_USER_SEARCH_FIELD="" \
LDAP_SEARCH_PAGE_SIZE=0 \
LDAP_SEARCH_SIZE_LIMIT=0 \
LDAP_GROUP_FILTER_ENABLE=false \
LDAP_GROUP_FILTER_OBJECTCLASS="" \
LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \
LDAP_GROUP_FILTER_GROUP_NAME="" \
LDAP_UNIQUE_IDENTIFIER_FIELD="" \
LDAP_UTF8_NAMES_SLUGIFY=true \
LDAP_USERNAME_FIELD="" \
LDAP_FULLNAME_FIELD="" \
LDAP_MERGE_EXISTING_USERS=false \
LDAP_EMAIL_FIELD="" \
LDAP_EMAIL_MATCH_ENABLE=false \
LDAP_EMAIL_MATCH_REQUIRE=false \
LDAP_EMAIL_MATCH_VERIFIED=false \
LDAP_SYNC_USER_DATA=false \
LDAP_SYNC_USER_DATA_FIELDMAP="" \
LDAP_SYNC_GROUP_ROLES="" \
LDAP_DEFAULT_DOMAIN="" \
LDAP_SYNC_ADMIN_STATUS="" \
LDAP_SYNC_ADMIN_GROUPS="" \
LDAP_SYNC_ORGANIZATIONS=false \
LDAP_SYNC_ORGANIZATIONS_GROUPS="" \
LDAP_SYNC_TEAMS=false \
LDAP_SYNC_TEAMS_GROUPS="" \
HEADER_LOGIN_ID="" \
HEADER_LOGIN_FIRSTNAME="" \
HEADER_LOGIN_LASTNAME="" \
HEADER_LOGIN_EMAIL="" \
HEADER_LOGIN_TRUSTED_IPS="" \
HEADER_LOGIN_TRUSTED_PROXIES="" \
LOGOUT_WITH_TIMER=false \
LOGOUT_IN="" \
LOGOUT_ON_HOURS="" \
LOGOUT_ON_MINUTES="" \
CORS="" \
CORS_ALLOW_HEADERS="" \
CORS_EXPOSE_HEADERS="" \
DEFAULT_AUTHENTICATION_METHOD="" \
PASSWORD_LOGIN_ENABLED=true \
CAS_ENABLED=false \
CAS_BASE_URL="" \
CAS_LOGIN_URL="" \
CAS_VALIDATE_URL="" \
SAML_ENABLED=false \
SAML_PROVIDER="" \
SAML_ENTRYPOINT="" \
SAML_ISSUER="" \
SAML_CERT="" \
SAML_IDPSLO_REDIRECTURL="" \
SAML_PRIVATE_KEYFILE="" \
SAML_PUBLIC_CERTFILE="" \
SAML_IDENTIFIER_FORMAT="" \
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
SAML_ATTRIBUTES="" \
OAUTH_GOOGLE_ENABLED=false \
OAUTH_GOOGLE_CLIENT_ID="" \
OAUTH_GOOGLE_SECRET="" \
OAUTH_GOOGLE_SECRET_FILE="" \
OAUTH_GITHUB_ENABLED=false \
OAUTH_GITHUB_CLIENT_ID="" \
OAUTH_GITHUB_SECRET="" \
OAUTH_GITHUB_SECRET_FILE="" \
OAUTH_FACEBOOK_ENABLED=false \
OAUTH_FACEBOOK_APP_ID="" \
OAUTH_FACEBOOK_SECRET="" \
OAUTH_FACEBOOK_SECRET_FILE="" \
OAUTH_TWITTER_ENABLED=false \
OAUTH_TWITTER_CONSUMER_KEY="" \
OAUTH_TWITTER_SECRET="" \
OAUTH_TWITTER_SECRET_FILE="" \
OAUTH_METEOR_DEVELOPER_ENABLED=false \
OAUTH_METEOR_DEVELOPER_CLIENT_ID="" \
OAUTH_METEOR_DEVELOPER_SECRET="" \
OAUTH_METEOR_DEVELOPER_SECRET_FILE="" \
OAUTH_WEIBO_ENABLED=false \
OAUTH_WEIBO_CLIENT_ID="" \
OAUTH_WEIBO_SECRET="" \
OAUTH_WEIBO_SECRET_FILE="" \
OAUTH_MEETUP_ENABLED=false \
OAUTH_MEETUP_CLIENT_ID="" \
OAUTH_MEETUP_SECRET="" \
OAUTH_MEETUP_SECRET_FILE="" \
OAUTH_PROVIDERS_LOGIN_STYLE="popup" \
OAUTH_PROVIDERS_MERGE_EXISTING_USERS=false \
PASSWORDLESS_ENABLED=false \
ORACLE_OIM_ENABLED=false \
WAIT_SPINNER="" \
WRITABLE_PATH=/data \
S3="" \
MAIL_SERVICE_PASSWORD_FILE="" \
MONGO_PASSWORD_FILE="" \
S3_SECRET_FILE=""
# Where this image's Node.js comes from is decided by the SAME script the .zip
# bundles and the snap use - official nodejs.org, then unofficial-builds, then
# wekan/node-patches - so the image and the bundle of the same architecture can
# never end up on Node.js from different places. It is copied in rather than
# reimplemented here; a second copy of that order would drift from the first.
COPY --chmod=755 releases/resolve-node-source.sh /tmp/resolve-node-source.sh
# It asks nodejs.org and github.com which builds exist, through releases/fetch.sh
# - which retries a 503 instead of reading it as "that build does not exist".
# The two travel together: without this line the resolve step dies on the first
# lookup. tests/releaseDownloads.test.cjs pins the pair.
COPY --chmod=755 releases/fetch.sh /tmp/fetch.sh
COPY --chmod=755 releases/check-telemetry.py /tmp/check-telemetry.py
COPY --chmod=755 releases/prepare-bundle-npm.mjs /tmp/prepare-bundle-npm.mjs
# The bundle's `npm install` leaves node-gyp's whole tree - 83 of the 120
# packages in programs/server/node_modules - in a bundle that compiles nothing at
# run time, and a scan of the published image reads it as what it is. The same
# script runs in every per-arch leg of release-all.yml, so the .zip bundles and
# this image are pruned identically. tests/imageBuildOnlyModules.test.cjs pins it.
COPY --chmod=755 releases/prune-build-only-modules.mjs /tmp/prune-build-only-modules.mjs
# Its companion, and its manifest: the npm packages Meteor's own packages bundle,
# which nothing in package.json can reach. The .zip is built with these already
# applied; what this run has to redo is the part `npm install` puts back.
COPY --chmod=755 releases/bump-bundle-npm-deps.mjs /tmp/bump-bundle-npm-deps.mjs
COPY --chmod=644 releases/bundle-npm-security-bumps.json /tmp/bundle-npm-security-bumps.json
# And the third of the same kind: WeKan talks DDP over sockjs on every platform,
# so no bundle carries uWebSockets.js. ddp-server requires that module only
# inside the uws transport's setup(), which a sockjs server never calls, and it
# is 121M of prebuilt binaries for OS/CPU/ABI combinations one machine cannot
# use. The entrypoint coerces DDP_TRANSPORT=uws to sockjs, so an existing
# compose file that asks for uws keeps working rather than crash-looping.
COPY --chmod=755 releases/bundle-trim.mjs /tmp/bundle-trim.mjs
# Its companion: the same idea applied to programs/server/npm/node_modules.
COPY --chmod=755 releases/prune-unreachable-npm.mjs /tmp/prune-unreachable-npm.mjs
RUN <<EOR
set -o xtrace
# Fail hard on any error so a missing release zip / failed download can never
# produce a "successful" image with an empty /build (Cannot find /build/main.js).
set -eo pipefail
# Create Wekan user. --create-home is required because --system users do not
# get a home directory by default; without it /home/wekan never exists and the
# later `chown ... /home/wekan/` aborts the build (now that set -e is active).
useradd --user-group --system --create-home --home-dir /home/wekan wekan
# OS Updates
apt-get update --assume-yes
apt-get upgrade --assume-yes
apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
# Runtime MIME detection: keep this outside BUILD_DEPS so cleanup retains it.
apt-get install --assume-yes --no-install-recommends file
# Multi-arch mapping: Docker TARGETARCH -> WeKan's own platform name, which is
# both the bundle .zip's name and what resolve-node-source.sh is asked about.
# amd64/arm64 have MongoDB Community; ppc64le/s390x/riscv64 have no MongoDB
# server and ship FerretDB v1 instead (the ferretdb binary is baked into their
# .zip and started by wekan-entrypoint.sh). Debian's 32-bit ARM port is armhf
# (ARMv7, VFPv3-D16), which is what linux/arm/v7 runs.
case "${TARGETARCH}" in
"amd64") WEKAN_ARCH="amd64" ;;
"arm64") WEKAN_ARCH="arm64" ;;
"ppc64le") WEKAN_ARCH="ppc64le" ;;
"s390x") WEKAN_ARCH="s390x" ;;
"riscv64") WEKAN_ARCH="riscv64" ;;
"386") WEKAN_ARCH="i386" ;;
# BOTH linux/arm/v6 and linux/arm/v7 arrive here as TARGETARCH=arm - the CPU
# generation is in TARGETVARIANT, not in TARGETARCH - so this branch MUST read
# the variant. Mapping "arm" straight to armhf, as it did, would hand an ARMv6
# board (Raspberry Pi 1, Zero) the armhf bundle, which is built to Debian's
# ARMv7-A baseline and whose instructions that board cannot execute.
"arm")
case "${TARGETVARIANT}" in
"v6") WEKAN_ARCH="armv6" ;;
# v7, and an unset variant, are Debian's armhf port: ARMv7-A,
# VFPv3-D16 hard-float. That is what linux/arm/v7 runs.
"v7"|"") WEKAN_ARCH="armhf" ;;
# v5 is armel: FerretDB and the MongoDB tools publish it (they are Go),
# but Node.js does not exist for ARMv5 at all, so there is no bundle to
# put in an image and refusing is the only honest answer.
*) echo "Unsupported 32-bit ARM variant: ${TARGETVARIANT} (only v6 and v7 have a WeKan bundle)"; exit 1 ;;
esac ;;
*) echo "Unsupported architecture: ${TARGETARCH}${TARGETVARIANT:+/${TARGETVARIANT}}"; exit 1 ;;
esac
# Node.js installation - official nodejs.org, then unofficial-builds.nodejs.org,
# then wekan/node-patches, in that order, for EVERY architecture.
#
# The choice is made by releases/resolve-node-source.sh, copied in above and used
# by the .zip bundles and the snap as well, so the image and the bundle of one
# architecture always carry the same Node.js from the same place. It is asked
# about the MAJOR, so a CPU whose newest build lags a release still gets its
# newest: it answers with the exact file, what shape that file is, and the
# SHA256 the source published for it.
#
# npm is arch-independent JavaScript, so it is grafted from the official amd64
# tarball of the version that was resolved - a build-time tool, not the shipped
# node. (node-patches publishes a bare node binary and no npm at all, which is
# why npm is fetched separately rather than taken from the archive above.)
cd /tmp
NODE_MAJOR="${NODE_VERSION#v}"; NODE_MAJOR="${NODE_MAJOR%%.*}"
if ! NODE_META="$(bash /tmp/resolve-node-source.sh "${WEKAN_ARCH}" "${NODE_MAJOR}")"; then
echo "No Node.js ${NODE_MAJOR}.x for ${WEKAN_ARCH} at nodejs.org, unofficial-builds.nodejs.org or wekan/node-patches, so this image cannot be built for it. Build node-${WEKAN_ARCH} in wekan/node-patches, or drop ${TARGETARCH} from the image's platform list." >&2
exit 1
fi
eval "${NODE_META}"
echo "Node.js ${node_full} for ${WEKAN_ARCH}: ${node_from} (${node_url})"
wget --tries=20 --waitretry=20 --retry-on-http-error=403,500,502,503 -O node-download "${node_url}"
echo "${node_sha256} node-download" | sha256sum -c -
case "${node_kind}" in
binary)
cp node-download /usr/local/bin/node
;;
tar.xz|tar.gz|tar)
# bsdtar (libarchive-tools) is installed below for the bundle; plain tar
# here, letting it detect the compression rather than naming it.
tar -xf node-download --no-same-owner "${node_member}"
cp "${node_member}" /usr/local/bin/node
;;
*)
echo "resolve-node-source.sh returned an unknown kind '${node_kind}'." >&2
exit 1
;;
esac
chmod +x /usr/local/bin/node
# npm + npx from the official amd64 tarball of the SAME version; extract only
# those paths, not the amd64 node binary.
# Same flags as the download above: a bare wget treats a 503 as fatal, and
# nodejs.org has them.
wget --tries=20 --waitretry=20 --retry-on-http-error=403,500,502,503 \
"https://nodejs.org/dist/${node_full}/node-${node_full}-linux-x64.tar.gz"
wget --tries=20 --waitretry=20 --retry-on-http-error=403,500,502,503 \
"https://nodejs.org/dist/${node_full}/SHASUMS256.txt"
grep " node-${node_full}-linux-x64.tar.gz\$" SHASUMS256.txt | sha256sum -c -
tar xzf "node-${node_full}-linux-x64.tar.gz" -C /usr/local --strip-components=1 --no-same-owner \
"node-${node_full}-linux-x64/lib/node_modules/npm" \
"node-${node_full}-linux-x64/bin/npm" \
"node-${node_full}-linux-x64/bin/npx"
rm -rf node-download "node-${node_full}-linux-x64.tar.gz" "node-${node_full}-linux-x64" SHASUMS256.txt
ln -s "/usr/local/bin/node" "/usr/local/bin/nodejs"
# NPM configuration
npm install -g npm@${NPM_VERSION}
chown --recursive wekan:wekan /home/wekan/
# Temporary Tar swap for Meteor bundle
cp $(which tar) $(which tar)~
ln -sf $(which bsdtar) $(which tar)
# WeKan Bundle Installation
mkdir -p /home/wekan/app
cd /home/wekan/app
# Retry the release-asset download: even though the CI `docker` job needs the
# `release` job (so the asset is already uploaded), GitHub's
# releases/download/<tag>/<asset> URL can briefly return 404 right after upload
# (CDN/propagation lag). A plain wget treats 404 as fatal, which failed the
# build; retry on transient HTTP errors so propagation lag no longer breaks it.
WEKAN_ZIP_URL="https://github.com/wekan/wekan/releases/download/v${VERSION}/wekan-${VERSION}-${WEKAN_ARCH}.zip"
wget --tries=20 --waitretry=20 --retry-on-http-error=404,403,500,502,503 "${WEKAN_ZIP_URL}" \
|| { echo "Failed to download ${WEKAN_ZIP_URL} after retries"; exit 8; }
unzip "wekan-${VERSION}-${WEKAN_ARCH}.zip"
rm "wekan-${VERSION}-${WEKAN_ARCH}.zip"
node /tmp/prepare-bundle-npm.mjs ./bundle
npm install --prefix ./bundle/programs/server
# node-gyp and @mapbox/node-pre-gyp compiled nothing here - every native module
# in the bundle is a prebuilt .node - and nothing in boot.js reaches them. Their
# tree is what shipped `tar` 6.2.1 (CRITICAL) and npm's networking stack to the
# published image, so it goes now that the install is done.
node /tmp/prune-build-only-modules.mjs ./bundle
# The .zip already carries the bumped meteor/ tree; this install put back
# meteor-dev-bundle's own underscore 1.13.7 pin (CVE-2026-27601) over it, so the
# same pass runs here.
node /tmp/bump-bundle-npm-deps.mjs ./bundle
# No uWebSockets.js, no legacy client, no source maps: this image runs sockjs,
# serves web.browser to every browser, and has no debugger attached to it.
node /tmp/bundle-trim.mjs ./bundle --transport sockjs --drop-legacy-client
# And the npm tree rspack cannot tree-shake, because Atmosphere packages load it
# through Npm.require(). Only what it can prove nothing requires.
node /tmp/prune-unreachable-npm.mjs ./bundle
python3 /tmp/check-telemetry.py --bundle ./bundle
mv /home/wekan/app/bundle /build
# The .zip bundle now ships a self-contained launcher + its own Node.js for the
# offline downloads; the Docker image installs its own Node and uses
# wekan-entrypoint.sh, so drop the redundant bundled node + launchers. Keeps
# /build/ferretdb (used by the entrypoint) and the per-arch MongoDB Database Tools
# (bsondump, mongodump, mongorestore, … from wekan/mongo-tools-patches, embedded in the
# bundle) for backup/restore inside the container. Saves ~80 MB per arch.
rm -f /build/node /build/start-wekan.sh /build/start-wekan.bat
# Restore original tar
mv $(which tar)~ $(which tar)
# Cleanup
# npm is a BUILD tool in this image and nothing else: the only thing it does is
# the `npm install` above, and the container starts bash + wekan-entrypoint.sh,
# which never calls it. Shipping it shipped npm's own bundled dependencies - its
# `tar` (CRITICAL), `sigstore`, `@sigstore/*`, `ip-address`, `brace-expansion` -
# as image content that no code path can reach. node itself stays, because that
# is what runs WeKan.
rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx
# Remove unused Go-based pebble binary shipped by base image to reduce CVE surface.
apt-get remove --purge --assume-yes pebble || true
rm -f /usr/bin/pebble
apt-get remove --purge --assume-yes ${BUILD_DEPS}
apt-get autoremove --assume-yes
apt-get clean --assume-yes
rm -Rf /tmp/*
rm -Rf /var/lib/apt/lists/*
rm -Rf /home/wekan/app
mkdir -p /data
chown wekan:wekan --recursive /data
EOR
# Database-backend selector entrypoint. Every arch's bundle now ships a FerretDB
# binary (baked into the .zip and moved to /build/ferretdb above); this script
# starts FerretDB v1 (SQLite) or leaves the DB external based on WEKAN_DB and the
# /build/.ferretdb-default marker (present only on MongoDB-less arches). See
# releases/ferretdb/wekan-entrypoint.sh.
COPY --chmod=755 releases/ferretdb/wekan-entrypoint.sh /build/wekan-entrypoint.sh
# #6492: standalone "recovering data" page the entrypoint serves as a brief bridge on
# the web port while a just-restored FerretDB comes back up during a data recovery.
COPY --chmod=644 releases/ferretdb/recovery-bridge.mjs /build/recovery-bridge.mjs
# #6595: the entrypoint asks this whether the database answers yet, so it can
# serve the "waiting for database" page instead of leaving the web port unbound
# for a reverse proxy to time out on.
COPY --chmod=644 releases/ferretdb/db-ready.mjs /build/db-ready.mjs
USER wekan
ENV PORT=8080
EXPOSE $PORT
STOPSIGNAL SIGKILL
WORKDIR /build
CMD ["bash", "/build/wekan-entrypoint.sh"]