Call write_missing_content_files() at the start of start_by_compose_file()
so bind-mounted content files exist before `docker compose up` runs, which
would otherwise create them as empty directories. This also covers
Dockerfile deployments to additional servers that lack the files.
Add a feature test for a Dockerfile deployment to an additional server,
and note in lessons that long-lived Horizon workers must flush static
caches in Queue::before.
- Stop the application on every server, also when one server is not functional.
- Write file mounts on every server of the application, not only on the main server.
- Deploy the exact commit, rollback flag and image tag of the main deployment to the
additional servers, so a rollback reverts all servers.
- Pull the pushed image on additional servers for inline Dockerfile applications
instead of building it again on each server.
- Fail multi-server deployments without a Docker image name, also from webhooks and the API.
- Send one deployment notification after all servers finish (parent_deployment_uuid).
- Block persistent volumes on multi-server applications (API, Livewire, SQLite connect)
and warn in the deployment log for existing applications that have both.
- Remove dead code in push_to_docker_registry().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove tests that copy app logic into the test, grep PHP source as
text, only check literals, constants, class_exists or PHP built-ins,
duplicate other tests, or are always skipped. Keep source checks that
guard security and authorization rules.
Rename a duplicate Pest helper so the full suite loads, drop the
missing v5/Browser path, and remove the CLAUDE.md symlink in favor of
AGENTS.md. Correct AGENTS.md: Dusk is not installed and the upgrade
script is scripts/upgrade.sh.
remote_process() stored the full shell command as plain text in
activity_log and kept it for 60 days. Database starts store the compose file
with the database passwords, imports store S3 keys, and the Cloudflare
tunnel setup stores its token.
- Store the command encrypted. The queue worker decrypts it right before the
SSH call and still reads plain commands of tasks queued before the upgrade.
- Remove the command when the task is finished: after a successful run, in
failed() when no attempt is left, and for a stopped import. A failed attempt
that the queue retries keeps it.
- Do not write a command preview into the error log.
- Migration: remove stored commands from activities that no longer run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Compose content files are written by a queued job. Deployments and
service starts now check all content files with one command before
docker compose up: a missing file or an empty directory (left by
Docker) is written; an existing file is kept; a directory with files
is kept with a visible warning.
- The server file sync that runs after every parse marked a file
storage as a directory and deleted its configured content when a
directory was at its path. A storage with content now keeps its
content; an empty directory is replaced with the file.
- Saving a storage no longer fails for an existing file, replaces an
empty directory, and no longer deletes a directory with files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>