(refactor) Quiet native installer output by default

Add step-based installer logging that reports pobsync actions as OK,
FAILED, or SKIPPED while suppressing noisy apt, pip, Django, and
systemd output during successful runs.

Show the captured output for the failed step when something breaks,
and add a --verbose flag to restore full command output for debugging.

Document the quieter installer behavior and verbose mode in the README
and development notes.
This commit is contained in:
2026-05-19 18:52:31 +02:00
parent 98d152da06
commit 96b91b2a69
3 changed files with 93 additions and 31 deletions

View File

@@ -60,7 +60,8 @@ sudo scripts/install-systemd \
Use `--no-install-os-packages` if you want to manage system packages yourself. Use `--force-env` only when you want the
installer to rewrite an existing `/etc/pobsync/pobsync.env`.
Use `--non-interactive` for scripted installs.
Use `--non-interactive` for scripted installs. Use `--verbose` when you want to see the underlying apt, pip, Django, and
systemd output.
For MariaDB support, add:

View File

@@ -72,11 +72,13 @@ Useful modes:
```
sudo scripts/install-systemd
sudo scripts/install-systemd --non-interactive
sudo scripts/install-systemd --verbose
sudo scripts/install-systemd --create-superuser --superuser-username admin
```
The installer should print a short completion summary with the control panel URL, Self Check reminder, and service log
commands. Keep that output user-facing rather than developer-facing.
commands. Keep normal output user-facing: pobsync step names with OK, FAILED, or SKIPPED. Full apt, pip, Django, and
systemd output belongs behind `--verbose` or in the failed step output.
## Migration Helpers

View File

@@ -16,6 +16,7 @@ WEB_BIND=${POBSYNC_WEB_BIND:-127.0.0.1:8010}
FORCE_ENV=0
INSTALL_OS_PACKAGES=1
WITH_NGINX=0
VERBOSE=0
INTERACTIVE=0
CREATE_SUPERUSER=ask
SUPERUSER_USERNAME=${POBSYNC_SUPERUSER_USERNAME:-}
@@ -72,6 +73,10 @@ while [ "$#" -gt 0 ]; do
FORCE_ENV=1
shift
;;
--verbose)
VERBOSE=1
shift
;;
--interactive)
INTERACTIVE=1
shift
@@ -128,6 +133,39 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi
run_step() {
label=$1
shift
if [ "$VERBOSE" -eq 1 ]; then
echo "==> $label"
"$@"
echo "OK: $label"
return
fi
printf '%-48s' "$label"
log_file=$(mktemp)
if "$@" >"$log_file" 2>&1; then
rm -f "$log_file"
echo "OK"
return
fi
echo "FAILED"
echo
echo "Output from failed step '$label':" >&2
cat "$log_file" >&2
rm -f "$log_file"
exit 1
}
note_step() {
label=$1
status=$2
printf '%-48s%s\n' "$label" "$status"
}
prompt_value() {
prompt=$1
default=$2
@@ -252,6 +290,7 @@ fi
install_os_packages() {
if [ "$INSTALL_OS_PACKAGES" -ne 1 ]; then
note_step "Install OS packages" "SKIPPED"
return
fi
@@ -263,8 +302,7 @@ install_os_packages() {
if [ "$INSTALL_EXTRAS" = "mariadb" ] || [ "$INSTALL_EXTRAS" = "[mariadb]" ] || [ "$INSTALL_EXTRAS" = ".[mariadb]" ]; then
packages="$packages default-libmysqlclient-dev build-essential pkg-config"
fi
apt-get update
apt-get install -y --no-install-recommends $packages
run_step "Install OS packages" sh -c "apt-get update && apt-get install -y --no-install-recommends $packages"
return
fi
@@ -294,19 +332,23 @@ if [ ! -f "$SOURCE_DIR/manage.py" ]; then
fi
if ! getent group "$SERVICE_GROUP" >/dev/null 2>&1; then
groupadd --system "$SERVICE_GROUP"
run_step "Create service group" groupadd --system "$SERVICE_GROUP"
else
note_step "Create service group" "OK"
fi
if ! id "$SERVICE_USER" >/dev/null 2>&1; then
useradd --system --home /var/lib/pobsync --shell /usr/sbin/nologin --gid "$SERVICE_GROUP" "$SERVICE_USER"
run_step "Create service user" useradd --system --home /var/lib/pobsync --shell /usr/sbin/nologin --gid "$SERVICE_GROUP" "$SERVICE_USER"
else
note_step "Create service user" "OK"
fi
mkdir -p /etc/pobsync /var/lib/pobsync /var/log/pobsync "$(dirname "$VENV_DIR")" "$APP_DIR" "$BACKUP_ROOT"
chown "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync
chmod 0750 /var/lib/pobsync /var/log/pobsync
run_step "Prepare directories" mkdir -p /etc/pobsync /var/lib/pobsync /var/log/pobsync "$(dirname "$VENV_DIR")" "$APP_DIR" "$BACKUP_ROOT"
run_step "Set state directory permissions" chown "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync
run_step "Set private directory modes" chmod 0750 /var/lib/pobsync /var/log/pobsync
if [ "$SOURCE_DIR" != "$APP_DIR" ]; then
rsync -a --delete \
run_step "Sync application files" rsync -a --delete \
--exclude .git \
--exclude .venv \
--exclude __pycache__ \
@@ -314,10 +356,12 @@ if [ "$SOURCE_DIR" != "$APP_DIR" ]; then
--exclude .mypy_cache \
--exclude var \
"$SOURCE_DIR"/ "$APP_DIR"/
else
note_step "Sync application files" "SKIPPED"
fi
python3 -m venv "$VENV_DIR"
"$VENV_DIR/bin/python" -m pip install --upgrade pip
run_step "Create Python virtualenv" python3 -m venv "$VENV_DIR"
run_step "Upgrade pip" "$VENV_DIR/bin/python" -m pip install --upgrade pip
case "$INSTALL_EXTRAS" in
"")
pip_target=$APP_DIR
@@ -336,7 +380,7 @@ case "$INSTALL_EXTRAS" in
exit 2
;;
esac
"$VENV_DIR/bin/python" -m pip install -e "$pip_target"
run_step "Install Python package" "$VENV_DIR/bin/python" -m pip install -e "$pip_target"
if [ ! -f "$ENV_FILE" ] || [ "$FORCE_ENV" -eq 1 ]; then
secret=$("$VENV_DIR/bin/python" -c "import secrets; print(secrets.token_urlsafe(48))")
@@ -359,8 +403,9 @@ POBSYNC_SCHEDULER_INTERVAL=60
EOF
chmod 0640 "$ENV_FILE"
chown "root:$SERVICE_GROUP" "$ENV_FILE"
echo "Wrote $ENV_FILE."
note_step "Write environment file" "OK"
else
note_step "Write environment file" "SKIPPED"
echo "Keeping existing $ENV_FILE. Use --force-env to rewrite it."
fi
@@ -382,49 +427,63 @@ install_unit() {
chmod 0644 "$dest"
}
install_unit "$APP_DIR/deploy/systemd/pobsync-web.service" /etc/systemd/system/pobsync-web.service
install_unit "$APP_DIR/deploy/systemd/pobsync-worker.service" /etc/systemd/system/pobsync-worker.service
install_unit "$APP_DIR/deploy/systemd/pobsync-scheduler.service" /etc/systemd/system/pobsync-scheduler.service
install_units() {
install_unit "$APP_DIR/deploy/systemd/pobsync-web.service" /etc/systemd/system/pobsync-web.service
install_unit "$APP_DIR/deploy/systemd/pobsync-worker.service" /etc/systemd/system/pobsync-worker.service
install_unit "$APP_DIR/deploy/systemd/pobsync-scheduler.service" /etc/systemd/system/pobsync-scheduler.service
}
systemctl daemon-reload
"$VENV_DIR/bin/python" "$APP_DIR/manage.py" migrate --noinput
"$VENV_DIR/bin/python" "$APP_DIR/manage.py" collectstatic --noinput --clear
chown -R "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync
run_step "Install systemd units" install_units
run_step "Reload systemd" systemctl daemon-reload
run_step "Run database migrations" "$VENV_DIR/bin/python" "$APP_DIR/manage.py" migrate --noinput
run_step "Collect static files" "$VENV_DIR/bin/python" "$APP_DIR/manage.py" collectstatic --noinput --clear
run_step "Finalize state permissions" chown -R "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync
superuser_exists=$("$VENV_DIR/bin/python" -c "import os; os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pobsync_server.settings'); import django; django.setup(); from django.contrib.auth import get_user_model; print('yes' if get_user_model().objects.filter(is_superuser=True).exists() else 'no')")
if [ "$CREATE_SUPERUSER" -eq 1 ]; then
if [ "$superuser_exists" = "yes" ]; then
echo "A Django superuser already exists; skipping superuser creation."
note_step "Create Django superuser" "SKIPPED"
elif [ -n "$SUPERUSER_USERNAME" ] && [ -n "$SUPERUSER_PASSWORD" ]; then
DJANGO_SUPERUSER_USERNAME=$SUPERUSER_USERNAME \
DJANGO_SUPERUSER_EMAIL=$SUPERUSER_EMAIL \
DJANGO_SUPERUSER_PASSWORD=$SUPERUSER_PASSWORD \
run_step "Create Django superuser" env \
DJANGO_SUPERUSER_USERNAME="$SUPERUSER_USERNAME" \
DJANGO_SUPERUSER_EMAIL="$SUPERUSER_EMAIL" \
DJANGO_SUPERUSER_PASSWORD="$SUPERUSER_PASSWORD" \
"$VENV_DIR/bin/python" "$APP_DIR/manage.py" createsuperuser --noinput
echo "Created Django superuser '$SUPERUSER_USERNAME'."
chown -R "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync
run_step "Finalize superuser permissions" chown -R "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync
else
note_step "Create Django superuser" "SKIPPED"
echo "No superuser password was provided; create one later with:"
echo " sudo -u $SERVICE_USER $VENV_DIR/bin/python $APP_DIR/manage.py createsuperuser"
fi
elif [ "$superuser_exists" != "yes" ]; then
note_step "Create Django superuser" "SKIPPED"
echo "No Django superuser exists yet. Create one with:"
echo " sudo -u $SERVICE_USER $VENV_DIR/bin/python $APP_DIR/manage.py createsuperuser"
else
note_step "Create Django superuser" "SKIPPED"
fi
systemctl enable --now pobsync-web.service pobsync-worker.service pobsync-scheduler.service
run_step "Enable and start services" systemctl enable --now pobsync-web.service pobsync-worker.service pobsync-scheduler.service
if [ "$WITH_NGINX" -eq 1 ]; then
if ! command -v nginx >/dev/null 2>&1; then
note_step "Install nginx config" "SKIPPED"
echo "nginx is not installed; skipping nginx config." >&2
else
sed "s|@POBSYNC_SERVER_NAME@|$SERVER_NAME|g" "$APP_DIR/deploy/nginx/pobsync.conf" > /etc/nginx/sites-available/pobsync.conf
ln -sf /etc/nginx/sites-available/pobsync.conf /etc/nginx/sites-enabled/pobsync.conf
nginx -t
systemctl reload nginx
note_step "Install nginx config" "OK"
run_step "Validate nginx config" nginx -t
run_step "Reload nginx" systemctl reload nginx
fi
else
note_step "Install nginx config" "SKIPPED"
fi
systemctl --no-pager --full status pobsync-web.service pobsync-worker.service pobsync-scheduler.service || true
if [ "$VERBOSE" -eq 1 ]; then
systemctl --no-pager --full status pobsync-web.service pobsync-worker.service pobsync-scheduler.service || true
fi
echo
echo "pobsync installation complete."