Webhook icon
Schedule icon
Script icon
Docker icon
If icon
Create icon
SlackIncomingWebhook icon
Switch icon
Log icon

Hold Production Deploys While a Freeze Window Is Active

Holds production deploys during declared freeze windows, opens a GitHub issue, asks Slack for an emergency-approval label, and reports the next opening.

Categories
Infrastructure

Release freezes are usually written down in a calendar nobody consults at the moment a deploy is queued. The window exists, the change still ships, and the incident the freeze was meant to prevent happens anyway. This blueprint turns the written window into a gate. A webhook receives pull request opened, synchronize, and labeled events plus deployment events, a Python task parses the declared window against the current UTC time, and a frozen verdict files a tracking issue, asks the channel for an emergency-approval label, and records which event action arrived. Any failure escalates to the ops channel, and every run publishes the verdict, the window, and the days until the next opening as outputs a deploy pipeline can read.

How it works

  1. pull_request_and_deployment_events (io.kestra.plugin.core.trigger.Webhook, keyed with the DEPLOY_FREEZE_WEBHOOK_KEY secret) starts a run when a pull request opens, pushes new commits, gains a label, or when a deployment event lands. weekly_freeze_preflight (io.kestra.plugin.core.trigger.Schedule, 0 6 * * 1) ships with disabled: true and recomputes the upcoming windows for the calendar digest once you enable it.
  2. evaluate_freeze_window (io.kestra.plugin.scripts.python.Script on python:3.11-slim) reads the three inputs, accepts ISO dates as well as ISO timestamps, treats a bare end date as 23:59:59Z, and compares the window with the current UTC time. A bare range is invalid and yields INVALID_WINDOW with is_frozen false, an elapsed window rolls forward a year to report the next opening, and the verdict is printed as a single ::{"outputs": ...}:: line.
  3. freeze_gate (io.kestra.plugin.core.flow.If) branches on is_frozen.
  4. Inside the frozen branch, file_freeze_issue (io.kestra.plugin.github.issues.Create) opens a deploy-freeze and change-freeze labeled issue whose title carries the window, and request_emergency_approval (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) asks the channel to apply the emergency-approval label.
  5. route_event_action (io.kestra.plugin.core.flow.Switch on the event action value) then evaluates the event action once. The labeled case branches on the label itself: emergency-approval logs the allow path and any other label logs that it does not lift the freeze, opened and synchronize log why the change is held, and the default branch prints the pre-flight digest line for deployment events and the schedule.
  6. When no window covers the current time, log_deploy_allowed records the allow verdict, leaving proof the gate actually ran.
  7. The flow level errors handler (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) reports any failure with the execution id, because a gate that dies silently is indistinguishable from a clear window.

What you get

  • A hard block on deploys for holidays, incidents, and launches, driven by the window you declare instead of by someone remembering the calendar.
  • One labeled GitHub issue per frozen run whose title carries the exact window, so the block is assignable work rather than a chat thread.
  • A Slack request naming the emergency-approval label, so the bypass is a reviewed label instead of a verbal exception.
  • An audit line per event action, distinguishing emergency approvals, fresh pull requests, new commits, unrelated labels, and the weekly pre-flight digest.
  • Four flow outputs (verdict, is_frozen, window, days_until) that a deploy pipeline, dashboard, or downstream subflow can branch on directly.

Who it's for

  • Platform and release engineering teams that declare change freezes but have no gate enforcing them.
  • SRE groups that need a documented exception path for hotfixes during an incident window.
  • Product and infrastructure teams coordinating holiday freezes across several repositories.

Why orchestrate this with Kestra

A calendar entry cannot open a ticket, ask for an approval label, or tell a deploy pipeline why it was stopped. Kestra gives the freeze a webhook trigger, a conditional gate with an exclusive branch over the event action, real GitHub and Slack tasks, a failure path that pages instead of hiding, and typed outputs another flow can require before a deploy proceeds.

Prerequisites

  • A Kestra instance with the GitHub and Slack plugins available.
  • A repository that allows issues, in the repo input.
  • A GitHub token that can open issues in that repository.
  • A webhook registration pointing GitHub pull request and deployment events at the trigger URL.
  • An ISO date or timestamp pair for the window you want to enforce.

Secrets

  • DEPLOY_FREEZE_WEBHOOK_KEY: Key that authenticates the webhook trigger.
  • GITHUB_TOKEN: GitHub token used by the issue creation task.
  • SLACK_WEBHOOK_URL: Slack incoming webhook used for the approval request and failure alerts.

Quick start

  1. Add the three secrets above to your Kestra namespace.
  2. Set freeze_start, freeze_end, and repo to the window you are enforcing and the repository it protects.
  3. Run the flow manually and read the outputs, expect BLOCKED with is_frozen true when the window covers now, ALLOWED with a positive days_until before it opens, and INVALID_WINDOW for a malformed pair.
  4. Point the GitHub webhook at the pull_request_and_deployment_events trigger URL, then enable weekly_freeze_preflight if you also want the Monday digest.

How to extend

  • Chain this flow as a subflow in the deploy pipeline and block the release on verdict or is_frozen.
  • Maintain several windows by looping over a calendar file with io.kestra.plugin.core.flow.Loop and taking the union of the ranges.
  • Add an io.kestra.plugin.core.flow.Sleep plus a re-check before the issue is filed, so a one minute window never raises a ticket.
  • Replace the Slack webhook with a PagerDuty or email task for incident freezes, or mirror the verdict to a dashboard with io.kestra.plugin.core.http.Request.
  • Extend the exclusive branch with a closed case that logs the release resuming when the window lapses.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.