forked from WeblateOrg/docker
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathstart
More file actions
executable file
·383 lines (332 loc) · 12.9 KB
/
start
File metadata and controls
executable file
·383 lines (332 loc) · 12.9 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
#!/bin/sh
set -e
# shellcheck disable=SC1091
. /app/venv/bin/activate
# Allow sensitive settings to be defined in a file
# in order to support Docker secrets
if [ -n "${POSTGRES_PASSWORD_FILE}" ]; then
POSTGRES_PASSWORD=$(cat "$POSTGRES_PASSWORD_FILE")
export POSTGRES_PASSWORD
fi
if [ -n "${REDIS_PASSWORD_FILE}" ]; then
REDIS_PASSWORD=$(cat "$REDIS_PASSWORD_FILE")
export REDIS_PASSWORD
fi
if [ -z "$CLIENT_MAX_BODY_SIZE" ]; then
CLIENT_MAX_BODY_SIZE=1000m
export CLIENT_MAX_BODY_SIZE
fi
if [ -n "${WEBLATE_ADMIN_PASSWORD_FILE}" ]; then
WEBLATE_ADMIN_PASSWORD=$(cat "$WEBLATE_ADMIN_PASSWORD_FILE")
export WEBLATE_ADMIN_PASSWORD
fi
if [ -n "${WEBLATE_EMAIL_HOST_PASSWORD_FILE}" ]; then
WEBLATE_EMAIL_HOST_PASSWORD=$(cat "$WEBLATE_EMAIL_HOST_PASSWORD_FILE")
export WEBLATE_EMAIL_HOST_PASSWORD
fi
if [ -n "${WEBLATE_AUTH_LDAP_BIND_PASSWORD_FILE}" ]; then
WEBLATE_AUTH_LDAP_BIND_PASSWORD=$(cat "$WEBLATE_AUTH_LDAP_BIND_PASSWORD_FILE")
export WEBLATE_AUTH_LDAP_BIND_PASSWORD
fi
echo "Starting Weblate $WEBLATE_VERSION..."
# Append GitHub SSH host keys from https://api.github.com/meta (HTTPS, verified JSON).
# Exits 0 on success, non-zero on failure.
fetch_github_ssh_keys_from_meta() {
/app/venv/bin/python << 'PY'
import json
import sys
import urllib.error
import urllib.request
try:
req = urllib.request.Request(
"https://api.github.com/meta",
headers={"User-Agent": "boost-weblate-docker-start"},
)
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.load(resp)
keys = data.get("ssh_keys") or []
if not keys:
sys.exit(1)
with open("/app/data/ssh/known_hosts", "a", encoding="utf-8") as f:
for key in keys:
key = key.strip()
if key:
f.write("github.com " + key + "\n")
except (OSError, urllib.error.URLError, ValueError, json.JSONDecodeError):
sys.exit(1)
PY
}
# Fail fast if the data volume is not writable before creating paths under it.
# (Otherwise mkdir -p /app/data/ssh, or host-side fixes like sudo mkdir /app/data/ssh,
# can error first and obscure this clearer diagnostic.)
if [ ! -w /app/data ]; then
echo "The /app/data volume is not writable, please adjust the permissions. Weblate is running as uid $(id -u)"
echo
echo "Please see https://github.com/WeblateOrg/docker/issues/2096 in case"
echo "the permissions on the volume are actually correct."
exit 1
fi
# SSH data dir (keys, known_hosts)
mkdir -p /app/data/ssh
# Fix permissions on SSH private key.
# Fails silently if the file doesn't exist yet.
chmod 600 /app/data/ssh/id_rsa 2> /dev/null || true
chmod 600 /app/data/ssh/id_ed25519 2> /dev/null || true
# Ensure GitHub host keys are in known_hosts so git clone via SSH works.
# Prefer GITHUB_KNOWN_HOSTS, else official keys from https://api.github.com/meta (HTTPS).
# If both are unavailable, fail closed (do not use ssh-keyscan: unauthenticated host keys).
if ! grep -q "github.com" /app/data/ssh/known_hosts 2> /dev/null; then
if [ -n "${GITHUB_KNOWN_HOSTS:-}" ]; then
printf '%s\n' "$GITHUB_KNOWN_HOSTS" >> /app/data/ssh/known_hosts
elif fetch_github_ssh_keys_from_meta; then
:
else
>&2 echo "Error: GitHub SSH host keys are missing and could not be fetched."
>&2 echo "Set GITHUB_KNOWN_HOSTS to GitHub's published SSH host keys, or fix network access to https://api.github.com/meta."
exit 1
fi
fi
# Generate secret
if [ ! -s /app/data/secret ]; then
echo "Generating Django secret..."
# https://github.com/django/django/blob/1.10.2/django/utils/crypto.py#L54-L56
/app/venv/bin/python -c "from django.utils.crypto import get_random_string; print(get_random_string(50))" > /app/data/secret
fi
# Generate self-signed SAML key
# This has to be done early as it is used from the settings_docker.py
if [ -n "$WEBLATE_SAML_IDP_URL" ]; then
if [ ! -f /app/data/ssl/saml.key ] || [ ! -f /app/data/ssl/saml.crt ]; then
echo "Generating self-signed certificate for SAML..."
mkdir -p /app/data/ssl
openssl req \
-new \
-newkey rsa:4096 \
-x509 \
-days 3652 \
-nodes \
-subj "/OU=Weblate/CN=$WEBLATE_SITE_DOMAIN/emailAddress=$WEBLATE_ADMIN_EMAIL" \
-out /app/data/ssl/saml.crt \
-keyout /app/data/ssl/saml.key
fi
fi
# For openshift, create an account in /etc/passwd
# @see https://docs.okd.io/latest/creating_images/guidelines.html
if ! whoami > /dev/null 2>&1; then
if [ -w /etc/passwd ]; then
echo "${USER_NAME:-weblate}:x:$(id -u):0:${USER_NAME:-weblate} user:${HOME}:/sbin/nologin" >> /etc/passwd
fi
fi
if [ -z "$WEBLATE_SITE_DOMAIN" ]; then
echo "Missing WEBLATE_SITE_DOMAIN, please configure it"
exit 1
fi
# Export Weblate variables
export WEBLATE_CMD="/app/venv/bin/weblate"
export WEBLATE_PY_PATH="/app/data/python/customize"
# Provide sane default value
if [ -z "$POSTGRES_SSL_MODE" ]; then
export POSTGRES_SSL_MODE="prefer"
fi
# Export variables for psql use
export PGPASSWORD="$POSTGRES_PASSWORD"
export PGSSLMODE="$POSTGRES_SSL_MODE"
# Update the time zone
if [ -w /tmp/localtime ]; then
zonefile="/usr/share/zoneinfo/$WEBLATE_TIME_ZONE"
if [ -n "$WEBLATE_TIME_ZONE" ] && [ -f "$zonefile" ]; then
cat "$zonefile" > /tmp/localtime
else
cat /usr/share/zoneinfo/Etc/UTC > /tmp/localtime
fi
fi
# Create fake Python app for customization
if [ ! -d "$WEBLATE_PY_PATH" ]; then
echo "Creating $WEBLATE_PY_PATH"
mkdir -p "$WEBLATE_PY_PATH/static"
touch "$WEBLATE_PY_PATH/__init__.py"
touch "$WEBLATE_PY_PATH/models.py"
fi
run_weblate() {
"$WEBLATE_CMD" "$@"
}
fail_dep() {
>&2 echo "$1 not running!"
>&2 echo
>&2 echo "$1 is expected to run as separate Docker container."
>&2 echo
>&2 echo "Please see our docs for more details:"
>&2 echo "https://docs.weblate.org/en/latest/admin/install/docker.html"
exit 1
}
if ! run_weblate check; then
>&2 echo "Failed to load configuration, please see errors above."
exit 1
fi
# Wait for redis
TIMEOUT=0
until run_weblate shell -c 'from django.core.cache import cache; cache.has_key("ping")' > /dev/null; do
>&2 echo "redis at ${REDIS_HOST:-cache} is unavailable - retrying $((30 - TIMEOUT))"
TIMEOUT=$((TIMEOUT + 1))
if [ $TIMEOUT -gt 30 ]; then
run_weblate shell -c 'from django.core.cache import cache; cache.has_key("ping")'
fail_dep redis
fi
sleep 1
done
if [ -z "$POSTGRES_HOST" ]; then
export POSTGRES_HOST=database
fi
if [ -z "$POSTGRES_PORT" ]; then
export POSTGRES_PORT=
fi
# Wait for database to get available
TIMEOUT=0
until run_weblate shell -c 'from django.db import connection; connection.cursor().execute("SELECT 1")' > /dev/null; do
>&2 echo "Database server at ${POSTGRES_HOST:-db} is unavailable - retrying $((30 - TIMEOUT))"
TIMEOUT=$((TIMEOUT + 1))
if [ $TIMEOUT -gt 30 ]; then
run_weblate shell -c 'from django.db import connection; connection.cursor().execute("SELECT 1")'
fail_dep PostgreSQL
fi
sleep 1
done
VERSION_CHECK=1
case $WEBLATE_DATABASES in
0 | [fF][aA][lL][sS][eE] | [nN][oO])
VERSION_CHECK=0
;;
esac
if [ $VERSION_CHECK -eq 1 ]; then
# Fetch server version
PGVERSION=$(psql -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -d "${POSTGRES_DB:-$POSTGRES_DATABASE}" -U "$POSTGRES_USER" -t -A -c 'SHOW server_version_num;')
unset PGPASSWORD
>&2 echo "Postgres $PGVERSION is up"
# Check if supported PostgreSQL version is used
if [ "$PGVERSION" -lt 120000 ]; then
>&2 echo "PostgreSQL 12 or newer is required to run Weblate"
>&2 echo "See https://docs.weblate.org/en/latest/admin/install/docker.html#upgrading-postgresql-container"
exit 1
fi
else
>&2 echo "Database is up"
fi
# Migrate database to current version and collect static files
if [ "$1" = "runserver" ]; then
DO_MIGRATE=1
# Select which services to run
SUPERVISOR_CONF=/run/supervisor.conf.d/
mkdir -p "$SUPERVISOR_CONF"
# Remove possible stale files from previous start
rm -f "$SUPERVISOR_CONF"/*
if [ -n "$WEBLATE_SERVICE" ]; then
if [ "$WEBLATE_SERVICE" != "celery-beat" ]; then
DO_MIGRATE=0
fi
ln -s "/etc/supervisor/conf.d/$WEBLATE_SERVICE.conf" "$SUPERVISOR_CONF"
else
# Symlink all non-celery services
find /etc/supervisor/conf.d -type f ! -name 'celery-*.conf' -print0 | xargs -0 -I '{}' ln -s '{}' "$SUPERVISOR_CONF"
if [ "${CELERY_SINGLE_PROCESS:-0}" -eq 1 ]; then
# Symlink single process service only
ln -s /etc/supervisor/conf.d/celery-single.conf "$SUPERVISOR_CONF"
ln -s /etc/supervisor/conf.d/celery-beat.conf "$SUPERVISOR_CONF"
else
# Symlink all celery services but the single process
find /etc/supervisor/conf.d -type f -name 'celery-*.conf' ! -name 'celery-single.conf' -print0 | xargs -0 -I '{}' ln -s '{}' "$SUPERVISOR_CONF"
fi
fi
if [ $DO_MIGRATE -eq 1 ]; then
echo "Starting database migration..."
if ! run_weblate migrate; then
echo
echo "Database migration has failed. Please check the error message above."
echo "Note: Upgrading across major versions is not supported. In case you are upgrading"
echo " from an 4.x version, please upgrade to 5.0.2 first."
echo
echo " Using following for the docker-compose.yaml:"
echo
echo " image: weblate/weblate:5.0.2.2"
exit 1
fi
# Create or update admin account
if [ -n "$WEBLATE_ADMIN_PASSWORD" ]; then
echo "Updating admin user password (unset WEBLATE_ADMIN_PASSWORD to disable)..."
run_weblate createadmin --password="$WEBLATE_ADMIN_PASSWORD" --update --email="$WEBLATE_ADMIN_EMAIL" --name="$WEBLATE_ADMIN_NAME"
else
run_weblate createadmin --email="$WEBLATE_ADMIN_EMAIL" --name="$WEBLATE_ADMIN_NAME" || true
fi
echo "Refreshing stats..."
run_weblate ensure_stats
fi
# Run with --clear to ensure all files are up to date
run_weblate collectstatic --noinput --clear
# Compress js and css
run_weblate compress --force --traceback
# wsgi socket dir
mkdir -p /run/granian/
# Celery pid, remove possible stale PID file
mkdir -p /run/celery
rm -f /run/celery/beat.pid
# Parse upstream X-Forwarded-For
case "$WEBLATE_IP_PROXY_HEADER" in
HTTP_X_FORWARDED_FOR)
WEBLATE_REALIP="
real_ip_header X-Forwarded-For;
set_real_ip_from 0.0.0.0/0;
"
;;
*)
WEBLATE_REALIP=""
;;
esac
# Detect SSL setup
if [ -f /app/data/ssl/privkey.pem ]; then
WEBLATE_BUILTIN_SSL="1"
if [ -z "$WEBLATE_IP_PROXY_HEADER" ]; then
# Use X-Forwarded-For from the built-in nginx
export WEBLATE_IP_PROXY_HEADER=HTTP_X_FORWARDED_FOR
fi
else
WEBLATE_BUILTIN_SSL=""
fi
# Make sure WEBLATE_ANUBIS_URL is set
: "${WEBLATE_ANUBIS_URL:=""}"
: "${WEBLATE_ENABLE_HTTPS:=""}"
# Generate nginx configuration
mkdir -p /tmp/nginx
/app/venv/bin/python /etc/nginx/generate-site.py /etc/nginx "$WEBLATE_URL_PREFIX" "$WEBLATE_REALIP" "$CLIENT_MAX_BODY_SIZE" "$WEBLATE_BUILTIN_SSL" "$WEBLATE_ANUBIS_URL" "$WEBLATE_SITE_DOMAIN" "$WEBLATE_ENABLE_HTTPS" > /tmp/nginx/weblate-site.conf
# Calculate number of processes, at least 2, at most 4, depending on CPU cores
if [ -z "$WEBLATE_WORKERS" ]; then
PROCESSORS=$(nproc)
WEBLATE_WORKERS=$((PROCESSORS < 2 ? 2 : PROCESSORS > 4 ? 4 : PROCESSORS))
echo "Auto-scaled to $WEBLATE_WORKERS processes, adjust by setting WEBLATE_WORKERS"
fi
# default values for celery options
: "${CELERY_MAIN_OPTIONS:="--concurrency $WEBLATE_WORKERS"}"
: "${CELERY_NOTIFY_OPTIONS:="--concurrency $WEBLATE_WORKERS"}"
: "${CELERY_TRANSLATE_OPTIONS:="--concurrency $WEBLATE_WORKERS"}"
: "${CELERY_MEMORY_OPTIONS:="--concurrency $((WEBLATE_WORKERS == 1 ? 1 : WEBLATE_WORKERS / 2))"}"
: "${CELERY_BACKUP_OPTIONS:="--concurrency 1"}"
: "${CELERY_BEAT_OPTIONS:=""}"
: "${CELERY_SINGLE_OPTIONS:=""}"
: "${WEB_WORKERS:="$((WEBLATE_WORKERS <= 3 ? 2 : WEBLATE_WORKERS / 2))"}"
: "${WEB_BLOCKING_THREADS:="$((WEBLATE_WORKERS * 2))"}"
: "${GRANIAN_ARGS:=""}"
export CELERY_MAIN_OPTIONS
export CELERY_NOTIFY_OPTIONS
export CELERY_TRANSLATE_OPTIONS
export CELERY_MEMORY_OPTIONS
export CELERY_BACKUP_OPTIONS
export CELERY_BEAT_OPTIONS
export CELERY_SINGLE_OPTIONS
export WEB_WORKERS
export WEB_BLOCKING_THREADS
export GRANIAN_ARGS
# Execute supervisor
exec /app/venv/bin/supervisord \
--loglevel="${SUPERVISOR_LOGLEVEL:-info}" \
--logfile="${SUPERVISOR_LOGFILE:-/dev/null}" \
--configuration=/etc/supervisor/supervisord.conf
fi
# Start the management command
run_weblate "$@"