mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-16 08:35:07 +00:00
f446398dfd
Somebody followed the README's Docker quickstart, which starts the development stack, and got three failures in a row with nothing to search for (#1627): the worker died once a second on a missing autoloader, the site answered a bare 500, and once dependencies were installed by hand the setup screen threw ViteManifestNotFoundException. None of that is wrong behaviour for a clone — vendor/ and public/build/ are deliberately not in git — but every one of those failures kept its cause to itself. The preflight guard exists to turn "this was never set up" into a sentence, and it runs before the autoloader precisely so it can. It now answers two more questions: dependencies not installed, and frontend not built. The dependency check goes first, before the .env one, because the fix that branch prints — php artisan key:generate — cannot itself run without the autoloader, so reporting the key first hands somebody a second and more confusing error. A running vite dev server counts as built: public/hot means the assets come from there, and blocking a developer mid-session would be worse than the exception this replaces. The worker and scheduler exec straight into artisan, so before composer install they died instantly and restarted forever, filling the log that had to be read to fix it. They now print what is missing and exit slowly, and recover on their own once it is there. The scheduler gains the restart policy the worker already had — without one it exits during that window and stays exited, and scheduled work then silently never happens. Rehearsed on a genuine clone of the public repository, following the reporter's exact path: worker prints instructions instead of fatals (2 restarts in 30s, not 30), the browser gets "ProjectSend is not installed yet" naming composer install, then "not configured yet", then "not built yet" naming npm run build, then the setup screen. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
63 lines
3.1 KiB
Bash
63 lines
3.1 KiB
Bash
#!/bin/sh
|
|
set -e
|
|
|
|
# php-fpm's master process must stay root: it forks workers as www-data
|
|
# itself via the pool config (php-fpm can't reopen its own error_log as
|
|
# non-root at startup). queue:work/schedule:work have no such fork model,
|
|
# so they drop straight to www-data (uid/gid matches the host user, see
|
|
# Dockerfile ARGs) instead of running as root for their whole lifetime.
|
|
|
|
# The app writes to these as www-data, and boot is the one moment this
|
|
# container runs as root — so repair ownership here, every boot. Root-run
|
|
# `docker compose exec` shells and uid changes across image rebuilds leave
|
|
# root-owned directories behind, and the first symptom is an opaque
|
|
# "mkdir(): Permission denied" 500 on upload. The file library's contents
|
|
# are deliberately NOT chowned recursively: -R over a large library on
|
|
# every boot is not free, and new writes only need the directories.
|
|
mkdir -p storage/app/files storage/app/private storage/app/public storage/app/uploads-tmp \
|
|
storage/framework/cache storage/framework/sessions storage/framework/testing storage/framework/views \
|
|
storage/logs bootstrap/cache
|
|
chown www-data:www-data storage storage/app storage/app/files storage/app/private storage/app/public
|
|
chown -R www-data:www-data storage/app/uploads-tmp storage/framework storage/logs bootstrap/cache
|
|
|
|
# The worker and the scheduler exec straight into artisan, which cannot run
|
|
# without the autoloader — so on a fresh clone, before `composer install`,
|
|
# both died instantly and `restart: unless-stopped` brought them back to die
|
|
# again, once a second, forever. The log that was filling up is the same one
|
|
# somebody needs to read to find out what to do (#1627).
|
|
#
|
|
# Exit rather than wait: this container has nothing useful to do until the
|
|
# dependencies exist, and compose will keep retrying — but slowly enough to
|
|
# leave the log readable, and it recovers on its own the moment they appear.
|
|
if [ "$1" != "php-fpm" ] && [ ! -f vendor/autoload.php ]; then
|
|
echo "projectsend: the PHP dependencies are not installed — there is no vendor/autoload.php." >&2
|
|
echo "projectsend: run 'docker compose exec app composer install' (see CONTRIBUTING.md)." >&2
|
|
sleep 15
|
|
|
|
exit 1
|
|
fi
|
|
|
|
# First-boot bootstrap runs only for the FPM service (the worker execs
|
|
# straight through) and only when the app is actually installed.
|
|
if [ "$1" = "php-fpm" ] && [ -f vendor/autoload.php ]; then
|
|
# The same command the official image and update.sh run — migrate,
|
|
# roles, storage link, compiled caches, workers. See
|
|
# UpdateInstallation; there is deliberately only one definition of it.
|
|
su-exec www-data php artisan projectsend:update
|
|
|
|
# Unattended provisioning: create the first administrator from the
|
|
# environment. Without these, the web setup screen prompts instead.
|
|
if [ -n "$ADMIN_EMAIL" ] && [ -n "$ADMIN_PASSWORD" ]; then
|
|
su-exec www-data php artisan projectsend:admin --if-none \
|
|
--name="${ADMIN_NAME:-Administrator}" \
|
|
--email="$ADMIN_EMAIL" \
|
|
--password="$ADMIN_PASSWORD"
|
|
fi
|
|
fi
|
|
|
|
if [ "$1" = "php-fpm" ]; then
|
|
exec "$@"
|
|
else
|
|
exec su-exec www-data "$@"
|
|
fi
|