From 7c5af8570a24ac00a1058308f6a2ec412f272db6 Mon Sep 17 00:00:00 2001 From: ignacionelson Date: Wed, 26 Aug 2026 18:09:23 -0300 Subject: [PATCH] Have the updater repair a worker that predates the zips queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Splitting zip builds onto their own queue (92a132d) left manual installs carrying the one job the release note has to do, and the failure it produces is the worst shape available: a worker still watching only `default` sends every email cheerfully and finishes no zip downloads, with nothing in any log to say why. An upgrade note is a poor place to put that, because it is read on a laptop and needed on a server. update.sh already finds projectsend-worker.service, so it now reads the unit's ExecStart and offers to add --queue=default,zips, keeping a copy of the original beside it. Before the restart, so the worker comes back on the command it is going to keep. Only the unambiguous case is rewritten: a queue:work line with no --queue at all, which consumes `default` and nothing else. A unit that already names its queues is somebody's deliberate arrangement, possibly with a second worker for zips, so that one is described rather than edited — and one that already includes zips is silently left alone. Exercised against five unit shapes rather than reasoned about: the plain command is rewritten and backed up, a declined prompt leaves it untouched with a warning, a unit already naming zips is a no-op, a custom queue list without zips warns instead of editing, and a unit that is not queue:work at all is ignored. The sed itself would double-append if it ran twice; it cannot, because the --queue= guard above it returns first, and both read the same first ExecStart line. --- CHANGELOG.md | 13 +++++----- update.sh | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c4aa57..75099cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,12 +41,13 @@ a version is cut. email behind it. Zip building now has a queue of its own, and the Docker images run a second background worker for it. - **Upgrade note, manual installs only:** your background worker has to be told about the new queue, - or zips will never finish and nothing will say why. Edit - `/etc/systemd/system/projectsend-worker.service` so the `ExecStart` line reads - `queue:work --queue=default,zips …`, then `sudo systemctl daemon-reload && sudo systemctl restart - projectsend-worker`. Docker installations need no change. See INSTALL.md for the two-worker setup - if you would rather keep the two kinds of work apart. + **Manual installs:** your background worker has to be told about the new queue, or zips will never + finish and nothing will say why. `update.sh` spots this and offers to fix the worker service for + you, keeping a copy of the old one — so for most people there is nothing to do but say yes. If you + update by hand, or your worker already names its own queues (the updater will say so rather than + edit a deliberate arrangement), add `zips` to its `--queue` list and reload systemd. Docker + installations need no change. See INSTALL.md for the two-worker setup if you would rather keep the + two kinds of work apart. ### Fixed diff --git a/update.sh b/update.sh index 731e22a8..6f15f6af 100755 --- a/update.sh +++ b/update.sh @@ -316,6 +316,69 @@ detect_services() { return 0 } +# The worker unit predates the zips queue, and a worker that is not +# watching it finishes no zip downloads while cheerfully sending every +# email — with nothing in any log to say why. That is the worst shape a +# regression can take, so the updater repairs the unit rather than +# leaving a note in a changelog nobody reads on a server. +# +# Only the case that is unambiguous: a command with no --queue at all, +# which consumes `default` and nothing else. A unit that already names +# queues is somebody's deliberate arrangement — possibly with a second +# worker for zips — so that one is described, not rewritten. +ensure_worker_watches_zips() { + [[ "${SYSTEMD:-0}" == "1" && -n "${WORKER_SERVICE:-}" ]] || return 0 + + local unit_file exec_line + unit_file="$(systemctl show -p FragmentPath --value "$WORKER_SERVICE" 2>/dev/null || true)" + + [[ -n "$unit_file" && -w "$unit_file" ]] || return 0 + + exec_line="$(sed -n 's/^ExecStart=//p' "$unit_file" | head -n 1)" + + case "$exec_line" in + *queue:work*) ;; + *) return 0 ;; + esac + + if [[ "$exec_line" == *"--queue="* ]]; then + case "$exec_line" in + *zips*) return 0 ;; + *) + warn "$WORKER_SERVICE names its own queues and does not include 'zips'." + warn "Zip downloads will never finish unless some worker watches that queue." + warn "Add 'zips' to its --queue list, or run a second worker with --queue=zips." + return 0 + ;; + esac + fi + + say "Your background worker predates the zips queue" + note "Building a zip download runs on its own queue now. $WORKER_SERVICE watches" + note "'default' only, so zips would never finish and nothing would say why." + note "Change: queue:work -> queue:work --queue=default,zips" + + if ! ask "Update $unit_file? [Y/n]" "y"; then + warn "Left alone. Zip downloads will not finish until a worker watches the zips queue." + + return 0 + fi + + cp -p "$unit_file" "$unit_file.projectsend-bak" 2>/dev/null || true + + # Anchored on the literal command so a unit with other arguments keeps + # them, and matched once: a Type=notify unit can carry several + # ExecStart lines and only the first is this worker. + if sed -i '0,/^ExecStart=/{s|\(^ExecStart=.*queue:work\)|\1 --queue=default,zips|}' "$unit_file"; then + systemctl daemon-reload || warn "Could not reload systemd. Run: systemctl daemon-reload" + note "Updated. A copy of the old unit is at $unit_file.projectsend-bak" + else + warn "Could not edit $unit_file. Add --queue=default,zips to its ExecStart line yourself." + fi + + return 0 +} + env_value() { local raw raw="$(first_line "$(sed -n "s/^$1=//p" "$INSTALL_DIR/.env" 2>/dev/null || true)")" @@ -612,6 +675,10 @@ Then reload PHP-FPM, and bring the site back with: php artisan up" || warn "Could not reload $FPM_SERVICE. Do it yourself, or PHP keeps serving the old code." if [[ -n "${WORKER_SERVICE:-}" ]]; then + # Before the restart, so the worker comes back on the command + # it is going to keep rather than needing a second bounce. + ensure_worker_watches_zips + say "Restarting $WORKER_SERVICE" systemctl restart "$WORKER_SERVICE" || warn "Could not restart $WORKER_SERVICE." fi