Commit Graph

2 Commits

Author SHA1 Message Date
ignacionelson 4524b75c9d Let a hosted plan switch zip downloads off with downloads.zip
A new capability, granted by both editions. Self-hosted installs keep zip
downloads as they are. A platform removes it through
PROJECTSEND_CAPABILITIES_DISABLED. The free shared instances do this,
because building an archive holds the zips worker, the disk and a CPU on
a server that thousands of accounts share.

- The three zip routes sit behind capability:downloads.zip, so a
  hand-made request gets a 404, not just a missing button.
- BuildZipDownloadJob refuses a build that was queued before the key went
  away. The row ends failed and is never stamped as started.
  StalledZipBuilds stays quiet when the key is off, so leftover rows
  raise no worker banner.
- The zip buttons are hidden. In the portal, the checkboxes and the
  selection bar are hidden too, since they exist only to pick files for a
  zip. Staff /files keeps its checkboxes, which also drive bulk edit.
- Archives already built are not touched. They expire on the normal
  purge schedule.
- A guard test walks the router. It fails if any route that reaches
  ZipDownloadsController, or dispatches the build job, lacks the
  middleware. No API route builds zips today.

The case goes last in the enum, because the control plane reads keys in
enum order.
2026-09-24 15:39:44 -03:00
ignacionelson 5d99ab94fd Say on screen when nothing is building zip downloads
Zip building moved onto its own queue, which a manual install's worker
has to be told about. update.sh repairs the service file and Docker is
unaffected, so the population left is somebody upgrading by hand who
skipped the release note — and for them the failure is the worst shape
available. Email keeps going out perfectly. Zip downloads never finish.
Nothing in any log says why, because nothing went wrong: the jobs sit on
a queue nobody is reading. The person who missed it has no reason to
suspect anything, so the notice has to go looking for them.

The application cannot see its own worker processes, only whether work
gets done, so the question is asked from the other end: was a build
requested that no worker ever picked up? That needs a record of when a
build *started*, which is what the new zip_downloads.started_at column
is — stamped before any of the work, so it says a worker had the row,
not that the row succeeded.

Two conditions, because either alone cries wolf. A build has waited past
five minutes and was never started, *and* no other build is in hand. The
second matters because one worker builds one archive at a time: a queue
behind a large build is a healthy queue, and its waiting rows look
exactly like abandoned ones until you notice something running. "In
hand" is bounded by the job's own timeout, so a worker that died holding
a build stops counting as alive an hour later.

The banner sits beside the stale-code one, on every staff page rather
than the dashboard alone, gated on view_system_info for the reason that
one already argues: a background worker not picking work up is a fact
about the machine, not a feature of an edition. It names the fix rather
than the symptom — "your worker command needs --queue=default,zips" —
because somebody reading that downloads are not being processed still
has to work out what to do about it.

Eight tests, covering both halves of the discrimination rather than just
the happy one: a queue waiting behind a live build stays quiet, and a
build held by a worker that died does not.

Translated into all sixteen locales in the same commit, since a release
is close and a banner nobody can read is worse than none.

Checked on screen as well as in assertions, with a real stalled row on
the dev stack: the banner renders, wraps, and reads correctly.
2026-08-27 00:12:42 -03:00