Skip to content

Changelog

Notable user-facing changes per release. Each GitHub release attaches the runnable jars (deploy-server-<tag>.jar, deploy-agent-<tag>.jar) — see Installation.

A file can be copied into the file that is already there

copy-file takes mode: in-place, which empties the destination and writes into it, the way cat source > target does. The default still writes a new file beside the destination and moves it over, so the destination is a different file afterwards — and a file mounted on its own into a container keeps showing the old one, so a service reading it there never saw the copy.

  • atomic is gone from copy-file; mode replaces it. atomic: false deleted the destination and created it again, which changed the file just as a replacement does. A plan that still sets it is refused by name.

A service is restarted without starting a process

Asking a supervisor to reload a service used to run the svc program. A host watching for unexpected launches inside a container reported every deploy — both the program and the helper the JVM starts to launch it.

The command is now written straight to the supervisor’s control channel, the named pipe at <service>/supervise/control, which is exactly what that program does; the supervisor still does the signalling. Nothing is executed, so those reports stop. A supervisor that is not running is noticed and reported rather than waited for, and the command is not delivered later when it comes back.

  • signal now takes term as well as hup.
  • A supervisor without a control channel is still supported: set serviceControl: program in the policy.
  • The agent no longer needs /usr/bin/svc mounted into its container.

Two fixes to the deploy plans added in 1.0-26

  • A deploy started in the moments right after an agent reconnects — following a restart or a network blip — could be refused with “cannot run step plans”, because the agent says what it can do immediately after connecting but that message still has to arrive. The server now gives a freshly connected agent a few seconds to answer. An agent that has already answered is not waited for, so a genuinely older agent is still refused at once.
  • A policy root whose parent directory is a symbolic link refused everything underneath it. Paths are resolved through links before they are compared with the roots, so the roots are now resolved the same way, even when the directory they name does not exist yet.

Neither affects an agent with no policy file, which keeps running the older commands.

Deploy without shell scripts on the agent

A deploy can now be described as a list of typed steps instead of a script to run on the agent. The server builds the plan from the alias; the agent checks the whole plan against a policy file on its own host and then carries it out inside its own process. The only program it still starts is the configured service-control binary.

  • Nine step types cover what the scripts did: fetch, unpack, write-file, copy-file, signal-service, sleep, check-version, wait-url and resolve-version.
  • Each agent host says what a plan may do there in AGENT_POLICY_FILE — the sources it may download from, the directories it may write to, the services it may signal, and how long it may take. An agent with no policy file refuses plans and runs only the older commands, so nothing changes until you install one.
  • An alias may declare the values it expects under params:. Those values are put into the parsed file rather than into its text, so a value can only ever become a value; a missing one is reported with a usage line, and one of the wrong shape is refused before anything runs.
  • Shared recipes in VERTX_RECIPES_DIR hold what every deployment of a kind does, so the same sequence is no longer copied once per application.
  • ?command=plan prints what an alias would do without running it, and the dashboard Aliases screen shows the recipe and steps of an alias.
  • A download now has to answer with the status it was asked for. A plain curl without -f stores an error page under the name of the artifact and lets the next step install it; that cannot happen any more.
  • The server keeps waiting for an agent as long as the agent reports progress, with an overall cap. A deploy that waits for a slow service used to be reported as failed while it was still running.

Nothing has to be converted at once: an alias without params: behaves exactly as before, both forms may appear in the same alias, and the server refuses to send a plan to an agent that has not reported it can run one. Update the agents before the aliases that use steps.

Dashboard SSE streams survive a reverse proxy

The /log/events and /agentlog/events tails used to send nothing — not even the response headers — until the first new line appeared, so nginx logged upstream timed out (110) while reading response header from upstream every 60 s on a quiet server and the browser reconnected in a loop. All three dashboard streams now flush their headers at once with a : connected comment, send a : keepalive comment after 15 s of silence, and set X-Accel-Buffering: no. See Behind a reverse proxy.

Deploy review webhook at deploy start (release)

A new outbound webhook notifies an external service the moment a deploy starts (the same moment as the 🛫 Telegram message), so the release can be analysed while it is still being rolled out:

  • Configured by DEPLOY_REVIEW_* — disabled by default, so nothing changes unless you set DEPLOY_REVIEW_ENABLED=true.
  • Body is {project, app, instance, old_version, new_version, deployed_at}; the token travels in an Authorization: Bearer header and is never logged or spooled.
  • project, app and instance come from three new fields in the alias diff: block; the versions are the ones the diff already resolves, so the webhook fires only for aliases with diff.enabled: true.
  • Delivery is durable and at-least-once through a third spool (QUEUE_DIR/deploy-review), with a queue="deploy-review" row on the dashboard Delivery card and in the queue metrics. 4xx answers are dead-lettered immediately instead of being retried.

Group diff commit messages by date in Telegram and Redmine (release)

The commit list posted during a deploy is now grouped by commit date instead of repeating the date on every line (see the Redmine workflow):

  • Telegram — each date appears once as a header line, with its commits listed as bullets underneath, newest day first.
  • Redmine — the comment table now has Issue | Subject columns, with each date rendered as a colspan header row.
  • Undated commits are grouped under a (no date) header, shown last.
  • Long Telegram messages still split at ~4000 characters, and the date header is re-emitted at the top of each continuation message so bullets stay under their date.

Keep the Delivery card visible as agent logs grow (release)

Fixes a layout bug on the Live dashboard: the Delivery card (Telegram/Redmine queue depths and send-latency percentiles) could be squeezed down to just its header as the agent-log stream accumulated, making the card look empty. The Delivery card now keeps its full height and the agent-log pane scrolls internally.

This is a dashboard-only fix — no server behavior or configuration changed. The card content was never wrong; it was simply being clipped by the layout.