New to Kestra?
Use blueprints to kickstart your first workflows.
Trigger a serialized deploy chain via a Kestra webhook and announce the validated result to a Zulip stream with retry.
This flow wraps a deployment in an announcement loop. An external system (CI, a release tool, a chatops command) calls a Kestra webhook to start the flow. A shell deploy step applies the change, captures the rolled-out revision, and derives its result from the real command exit code, then ZulipIncomingWebhook announces the result (service, version, environment, revision, status) to the engineering stream. A flow-level errors handler posts a separate failure notice if the deploy step exits non-zero, so the stream always reflects the true outcome.
The deploy step shown here is a runnable stand-in: it echoes the steps, emits a revision, and exits non-zero when the fail_deploy input is true, so you can exercise both the success and failure announcement paths before wiring in your real rollout. Replace the commands with your real rollout (for example kubectl apply and kubectl rollout status, an argocd app sync, or a helm upgrade), keeping the final ::{...}:: outputs line so the announcement can reference the revision and result. The base image is pinned to ubuntu:24.04; your real rollout needs an image (or a task runner) that actually contains your deploy CLI, since stock Ubuntu ships none of them.
concurrency: { limit: 1 } serializes the flow so two CI calls (or a caller's retry storm) cannot start overlapping deploys of the same service at once; concurrent webhook calls queue instead of racing. For per-service isolation, split into per-service flows or add labels and a queue key.service and environment are SELECT inputs and version is constrained by a validator regex, so a sloppy CI variable or a hostile webhook body cannot put arbitrary content into the channel that engineers trust as deployment truth.| toJson, so even if an input slips past validation, a stray quote or newline cannot break the JSON or inject markdown.timeout (PT15M) so a hung rollout status cannot wedge a worker indefinitely, and both Zulip tasks carry a retry. Note that ZulipIncomingWebhook does not raise on a non-200 response, so a lost success message does not by itself mean the deploy failed; confirm against the Kestra execution state.io.kestra.plugin.core.trigger.Webhook starts the flow when your release pipeline POSTs to the webhook URL with the secret key.deploy (shell) runs the rollout, exits non-zero on failure, and on success prints a Kestra outputs line exposing revision and result.announce renders those values plus the inputs into a markdown payload and POSTs it to Zulip on success.announce_failure posts a failure message if any task in the flow fails.ZULIP_WEBHOOK: the full Zulip incoming webhook URL, including the integration path and API key, in the form https://yourZulipDomain.zulipchat.com/api/v1/external/INTEGRATION_NAME?api_key=API_KEY. Used by both ZulipIncomingWebhook tasks to post to the stream.DEPLOY_WEBHOOK_KEY: the secret key segment for the Kestra Webhook trigger. The caller must include it in the trigger URL so only authorized systems can start the deploy.ZULIP_WEBHOOK secret.DEPLOY_WEBHOOK_KEY, and build the trigger URL https://<your-kestra>/api/v1/executions/webhook/company.platform/zulip-deployment-notification/<DEPLOY_WEBHOOK_KEY>.service SELECT and replace the shell commands with your real deployment commands on an image that contains your deploy CLI.service, version, and environment inputs).fail_deploy: true to confirm the failure path.{{ outputs.deploy.vars.revision }} and {{ outputs.deploy.vars.result }} carry the rollout details into the announcement.{{ outputs.announce.uri }}. Success is a message in the engineering stream plus a SUCCESS state on the announcing task.