feat(workflows): add auto-disable feature for workflows on consecutive failures
Deploy to Raspberry Pi / deploy (push) Canceled after 0s

- Introduced `disableOnConsecutiveFailures` option in workflow triggers to automatically disable workflows after reaching a specified failure threshold.
- Updated related functions to handle the new feature, including persistence of the disabled state and reloading of registries.
- Enhanced UI components to support the new option, allowing users to toggle the auto-disable feature in the workflow configuration.
This commit is contained in:
2026-08-21 05:37:24 +07:00
parent 46aa8ca327
commit 864427b45c
6 changed files with 113 additions and 12 deletions
@@ -117,10 +117,15 @@ export function TriggerCard({
function triggerSummary(trigger, owner) {
const type = trigger?.type;
const failure =
trigger.onConsecutiveFailures && trigger.onFailureWorkflow
? ` · onFailure@${trigger.onFailureWorkflow}`
: "";
/** @type {string[]} */
const failureParts = [];
if (trigger.onConsecutiveFailures && trigger.onFailureWorkflow) {
failureParts.push(`onFailure@${trigger.onFailureWorkflow}`);
}
if (trigger.disableOnConsecutiveFailures) {
failureParts.push("auto-disable");
}
const failure = failureParts.length ? ` · ${failureParts.join(", ")}` : "";
if (type === "HTTP") {
return `${trigger.method || "POST"} ${namespacedPath(owner || "owner", trigger.path || "/")}${failure}`;
}
@@ -516,6 +521,20 @@ function FailureAlertFields({ trigger, disabled, onChange, alertDestinations })
) : null}
</select>
</Field>
<label className="label cursor-pointer justify-start gap-3 py-0">
<input
type="checkbox"
className="checkbox checkbox-sm"
checked={Boolean(trigger.disableOnConsecutiveFailures)}
disabled={disabled}
onChange={(e) =>
onChange({ ...trigger, disableOnConsecutiveFailures: e.target.checked })
}
/>
<span className="label-text">
Disable this workflow when the consecutive failure threshold is reached
</span>
</label>
</div>
);
}
+15 -1
View File
@@ -144,6 +144,7 @@ export function newHttpTrigger() {
unauthorized: null,
onConsecutiveFailures: "",
onFailureWorkflow: "",
disableOnConsecutiveFailures: false,
};
}
@@ -159,6 +160,7 @@ export function newCronTrigger() {
unauthorized: null,
onConsecutiveFailures: "",
onFailureWorkflow: "",
disableOnConsecutiveFailures: false,
};
}
@@ -174,6 +176,7 @@ export function newWorkflowTrigger() {
unauthorized: null,
onConsecutiveFailures: "",
onFailureWorkflow: "",
disableOnConsecutiveFailures: false,
};
}
@@ -271,9 +274,16 @@ function normalizeTrigger(raw) {
"unauthorized",
"onConsecutiveFailures",
"onFailureWorkflow",
"disableOnConsecutiveFailures",
])
: type === "cron"
? new Set(["type", "schedule", "onConsecutiveFailures", "onFailureWorkflow"])
? new Set([
"type",
"schedule",
"onConsecutiveFailures",
"onFailureWorkflow",
"disableOnConsecutiveFailures",
])
: new Set(["type"]);
/** @type {Record<string, unknown>} */
const extra = {};
@@ -291,6 +301,7 @@ function normalizeTrigger(raw) {
? ""
: String(raw.onConsecutiveFailures),
onFailureWorkflow: readOnFailureWorkflow(raw),
disableOnConsecutiveFailures: raw.disableOnConsecutiveFailures === true,
auth: Array.isArray(raw.auth) ? raw.auth : null,
response: typeof raw.response === "string" ? raw.response : "",
unauthorized: raw.unauthorized ?? null,
@@ -334,6 +345,9 @@ function dumpFailureTriggerFields(t, out) {
if (typeof t.onFailureWorkflow === "string" && t.onFailureWorkflow.trim()) {
out.onFailureWorkflow = t.onFailureWorkflow.trim();
}
if (t.disableOnConsecutiveFailures === true) {
out.disableOnConsecutiveFailures = true;
}
}
function dumpTrigger(t) {