WorkingDirectory icon
Commands icon
Docker icon
Export icon
ArchiveDecompress icon
Clone icon
Log icon
If icon
Pause icon
PushFlows icon
SlackIncomingWebhook icon
Schedule icon

Backport Kestra UI hotfixes to a Git review branch

Detect UI hotfixes against a deployed Git baseline, backport approved flows to a review branch and verify the saved definitions.

Categories
CoreInfrastructure

Safety

A UI hotfix can disappear on the next Git deployment unless it is reviewed and saved in Git. This guard copies eligible live definitions to an incident branch for review.

Guard What it stops
Audited last-deployed commit Confusing Git-only changes with UI hotfixes
AHEAD IDs only; explicit protected IDs Backporting BEHIND, DIVERGED or protected definitions
dry_run: true, auto_remediate: false; approval defaults false Unattended writes
Fresh comparison after approval Applying a plan changed during the Pause
Incident branch and delete: false Writing the default branch or deleting repository files
Single execution; target and inventory caps Overlapping executions of this flow or oversized batches
Native namespace-text check Committing source that the native namespace regex could rewrite
Export and clone after push Reporting success when selected definitions or protected files differ

Keep external UI and Git writers paused during the push. PushFlows exports again and has no conditional version write; verification detects a concurrent change but cannot undo its review-branch commit.

How it works

  1. initial_export uses io.kestra.plugin.kestra.flows.Export; initial_unzip uses io.kestra.plugin.compress.ArchiveDecompress. initial_default, initial_baseline and initial_review use io.kestra.plugin.git.Clone to read Git.
  2. inspect compares SHA-256 hashes against baseline_commit: AHEAD means live changed while Git stayed at baseline; BEHIND means live stayed at baseline while Git changed; DIVERGED covers conflicting changes or live deletions; IN_SYNC means live and Git match. Live-only definitions qualify as AHEAD. CRLF and the top-level numeric revision line are normalized; scalar whitespace is preserved. Python joins local plugin artifacts because these plugins have no three-way hash comparison task.
  3. approve pauses for up to one hour. recheck repeats the reads and rejects any change to the compared definitions, repository files or target set.
  4. push_selected uses io.kestra.plugin.git.PushFlows with the exact selected IDs, no child namespaces and no deletes. verify re-exports and clones again, failing unless selected branch definitions match the approved UI hashes and protected files match the approved tree.

What you get

The inspect report artifact contains per-flow classification, hashes and pending targets. Successful backports leave the default branch untouched; rerunning against the same incident branch skips definitions already saved exactly. Task failures invoke the Slack handler; concurrency admission failures do not.

Who it's for

Platform teams that deploy Kestra flows from Git and need to preserve incident edits before the next deployment.

Why orchestrate this with Kestra

Native export and Git tasks keep remote reads and the write in the execution record alongside approval and verification.

Tested end to end

Tested on Kestra OSS 2.0.6 with Git plugin 4.3.0, Kestra plugin 2.1.1, Compress 1.3.10, Python scripts 1.16.0, Slack 2.1.1 and Gitea 1.22.6. Independent Git CLI diffs and live API source hashes showed two pending UI hotfixes reduced to zero: only their two files changed on the review branch, with the default commit unchanged. The fixture included Git-only edits, a conflict, a protected UI edit and a child namespace.

Case Execution Result
Healthy 68keX0LbCpFf1SLgJQS1H8 SUCCESS; no targets
Dry run 1mBPN9BZKbNhLB3DwLdSxh SUCCESS; two targets, no push
Declined ICHuS9iKLgzgm1t9kzbQr SUCCESS; no push
Approved 23xpTCZ8itQ498943hw8w5 SUCCESS; both branch hashes match live
Repeat 7O8ocU9spoiD7s0lHE5bBe SUCCESS; no new commit
Refused credentials 4nDU1hCH6pfUnARjCb3XJs FAILED at native push
Live edit during approval 20mUfgUbymxTHo2TqLVYm2 FAILED before push

Additional saved cases cover empty exports, a 105-flow inventory, inventory and target caps, a 100-character ID with Unicode and literal template text, unsafe namespace-line refusal and retry, a real Subflow backport, both sides of byte/file limits, symlink/ignore refusal, conflicting prior backports, live-export overflow and post-push verification failure with residual-branch recovery. Healthy, approved and stale-approval cases were repeated against a second fresh Kestra fixture.

Prerequisites

Run this guard from a separate operations Kestra instance with a Docker task runner and network access to the managed instance and Git server. The comparison tasks use pinned Python 3.12 and PyYAML 6.0.2. Grant the Kestra account export/read access to the managed namespace and the Git account read access plus permission to create and update the review branch; protect the default branch separately. Keep the audited full commit last deployed successfully as baseline_commit.

Use a flat Git directory containing only <flow-id>.yml files for the exact managed namespace. Repositories with symlinks, .kestraignore, conflicting prior backports or unrelated review-branch edits are refused. The retained Git index must be version 2 or 3; unsupported or corrupt indexes are refused. PushFlows uses an unescaped namespace regex even without targetNamespace; eligible sources with another matching namespace line, an indented match or a nonliteral match are refused before approval. Nonmatching nested Subflow namespaces remain usable.

Secrets

  • KESTRA_PASSWORD: password for the managed instance; default for the secret input.
  • GIT_TOKEN: Git password or token; default for the secret input.
  • SLACK_WEBHOOK_URL: failure destination, referenced by {{ secret('SLACK_WEBHOOK_URL') }}.

Inputs

Input Purpose / default
kestra_url, kestra_username, kestra_password Managed instance and credentials; URL defaults to http://localhost:8080
repository, git_username, git_token HTTP(S) Git URL and credentials
managed_namespace Exact namespace; company.production
baseline_commit Audited full 40-character last-deployed commit
review_branch Stable ui-backport/<incident> branch; reuse for retries
git_directory Flat flow directory; _flows
protected_flows IDs never selected; empty list
max_flows Inventory cap; 500, allowed 1 to 1000
max_targets Pending write cap; 10, allowed 1 to 100
dry_run, auto_remediate Safe defaults true, false

Quick start

  1. Configure secrets and credentials, confirm the baseline commit and protect the default branch.
  2. Run with safe defaults and inspect inspect's report.json artifact, including protected and DIVERGED rows.
  3. Freeze external writers, reuse the incident branch and run with auto_remediate: true and dry_run: false. Approve only the intended target set.
  4. Review the resulting Git diff before merging through your normal process. On failure, inspect the branch and rerun with the same baseline and branch after resolving the cause.

How to extend

Add a notification linking to the incident branch or connect successful verification to your Git provider's review workflow. The daily schedule is disabled and supplies safe-mode inputs.

Limits: this preserves UI edits in Git; it does not deploy or merge them. BEHIND and DIVERGED definitions are report-only. SyncFlows has no per-flow selector for a selective restore. Hashes compare source text rather than YAML semantics, so formatting changes can appear as drift. The baseline must be trustworthy. Limits are checked after plugin reads: each flow is capped at 1 MiB, repository files at 4 MiB each and the compared repository at 5000 files. Comparison tasks have two-minute timeouts and 256 MB memory limits. Concurrency applies only to this flow; hold external writers still during push and keep the operations instance separate from the managed instance.

Links

This blueprint was created by zkasuran.

See How

New to Kestra?

Use blueprints to kickstart your first workflows.