Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ public class CommandsTrigger extends AbstractTrigger
@Getter(AccessLevel.NONE)
private final AtomicBoolean lastMatched = new AtomicBoolean(false);

/**
* Evaluate the trigger by running the configured commands and generate a flow execution when the exit condition matches.
*
* <p>The method runs the commands once, evaluates the configured exit condition against the run output, and decides
* whether to emit based on the resolved edge mode: when edge is true, emit only on a transition from not-matched to
* matched; when edge is false, emit on every poll where the condition matches.</p>
*
* @param conditionContext context providing the RunContext and evaluation environment for rendering values
* @param context the trigger evaluation context (scheduling/trigger metadata)
* @return an Optional containing a generated Execution when the condition should emit, or an empty Optional otherwise
* @throws Exception if rendering or task execution fails
*/
@Override
public Optional<Execution> evaluate(ConditionContext conditionContext, TriggerContext context) throws Exception {
RunContext runContext = conditionContext.getRunContext();
Expand All @@ -135,6 +147,13 @@ public Optional<Execution> evaluate(ConditionContext conditionContext, TriggerCo
return Optional.of(TriggerService.generateExecution(this, conditionContext, context, out));
}

/**
* Execute the configured Commands task once and produce an Output summarizing the execution.
*
* @param runContext the execution context used to render the exit condition and run the task
* @return an Output containing the timestamp, rendered condition, exit code, and either captured vars (on success) or failure logs (on task failure)
* @throws Exception if rendering or task execution fails unexpectedly
*/
private Output runOnce(RunContext runContext) throws Exception {
Commands task = Commands.builder()
.taskRunner(Process.instance())
Expand All @@ -156,6 +175,16 @@ private Output runOnce(RunContext runContext) throws Exception {
}
}

/**
* Checks if the trigger condition matches the provided Output.
*
* The condition supports either an explicit exit code in the form `"exit N"` or a regular
* expression to search against the combined task variables and logs. If the condition is
* not a valid regex, a plain substring containment check is used as a fallback.
*
* @param out the execution output and rendered condition to evaluate
* @return `true` if the Output matches the configured condition, `false` otherwise
*/
private boolean matchesCondition(Output out) {
String cond = out.getCondition() == null ? "" : out.getCondition().trim();

Expand All @@ -177,6 +206,12 @@ private boolean matchesCondition(Output out) {
}
}

/**
* Builds a single string for condition matching by concatenating the output's vars (map) and logs, each followed by a newline when present.
*
* @param out the trigger output containing vars and logs
* @return the concatenated vars and logs as a string; an empty string if both are absent
*/
private String buildHaystack(Output out) {
StringBuilder sb = new StringBuilder();

Expand All @@ -190,6 +225,12 @@ private String buildHaystack(Output out) {
return sb.toString();
}

/**
* Retrieve the exit code from a ScriptOutput, returning null if it cannot be obtained.
*
* @param taskOutput the script output to read the exit code from
* @return the exit code, or `null` if the exit code is unavailable or an exception occurs while reading it
*/
private Integer safeExitCode(ScriptOutput taskOutput) {
try {
return taskOutput.getExitCode();
Expand All @@ -198,6 +239,12 @@ private Integer safeExitCode(ScriptOutput taskOutput) {
}
}

/**
* Retrieve the variables map from the given script task output or null if unavailable.
*
* @param taskOutput the script task output to read
* @return the variables map from the task output, or null if the variables cannot be obtained
*/
private Map<String, Object> safeVars(ScriptOutput taskOutput) {
try {
return taskOutput.getVars();
Expand All @@ -209,6 +256,14 @@ private Map<String, Object> safeVars(ScriptOutput taskOutput) {
private record ExtractedFailure(Integer exitCode, String logs) {
}

/**
* Extracts an exit code and captured logs from a RunnableTaskException by walking its cause chain
* and locating the first TaskException.
*
* @param e the RunnableTaskException whose cause chain will be inspected
* @return an ExtractedFailure containing the located exit code and logs; each field may be
* {@code null} if no TaskException is found or the corresponding value is unavailable
*/
private ExtractedFailure extractFailure(RunnableTaskException e) {
Integer exitCode = null;
String logs = null;
Expand Down Expand Up @@ -238,4 +293,4 @@ public static class Output implements io.kestra.core.models.tasks.Output {
private Map<String, Object> vars;
private String logs;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ Inline script content (multi-line string). This is the same 'script' concept as
@Getter(AccessLevel.NONE)
private final AtomicBoolean lastMatched = new AtomicBoolean(false);

/**
* Evaluate the trigger by executing the configured script and decide whether to emit an execution
* based on the rendered exit condition and the `edge` setting.
*
* If emission criteria are met, produces an Execution populated with the evaluated Output.
*
* @param conditionContext the condition evaluation context containing the RunContext
* @param context the trigger invocation context used when generating an Execution
* @return an Optional containing an Execution when the trigger should emit, or an empty Optional otherwise
*/
@Override
public Optional<Execution> evaluate(ConditionContext conditionContext, TriggerContext context) throws Exception {
RunContext runContext = conditionContext.getRunContext();
Expand All @@ -143,6 +153,17 @@ public Optional<Execution> evaluate(ConditionContext conditionContext, TriggerCo
return Optional.of(TriggerService.generateExecution(this, conditionContext, context, out));
}

/**
* Execute the configured script once and produce an Output snapshot for this poll.
*
* Renders the trigger's exitCondition using the provided RunContext, runs the script task,
* and returns an Output containing the poll timestamp, rendered condition, script exit code,
* any produced vars (on successful execution) or captured failure logs (on execution failure).
*
* @param runContext the execution context used to render properties and run the script
* @return an Output with timestamp, rendered condition, exitCode, vars (when available), and logs (on failure)
* @throws Exception if rendering or task execution fails unexpectedly
*/
private Output runOnce(RunContext runContext) throws Exception {
Script task = Script.builder()
.taskRunner(Process.instance())
Expand All @@ -166,6 +187,15 @@ private Output runOnce(RunContext runContext) throws Exception {
}
}

/**
* Determines whether the given output satisfies the trigger's exitCondition.
*
* Supports the special form "exit N" which matches when the output's exit code equals N.
* For other conditions, the condition is treated as a regular expression applied to the concatenation of the task's vars and logs; if the regex is invalid, falls back to a plain substring containment check. If either the rendered condition or the haystack is empty, the result is `false`.
*
* @param out the trigger execution output to evaluate
* @return `true` if the output matches the configured condition, `false` otherwise
*/
private boolean matchesCondition(Output out) {
String cond = out.getCondition() == null ? "" : out.getCondition().trim();

Expand All @@ -189,6 +219,12 @@ private boolean matchesCondition(Output out) {
}
}

/**
* Concatenates the trigger output's vars and logs into a single newline-separated string.
*
* @param out the trigger output containing optional vars and logs
* @return a string containing the vars (if present) followed by the logs (if present), each terminated by a newline; empty string if neither is present
*/
private String buildHaystack(Output out) {
StringBuilder sb = new StringBuilder();

Expand All @@ -202,6 +238,12 @@ private String buildHaystack(Output out) {
return sb.toString();
}

/**
* Extracts the exit code from the given ScriptOutput, or returns null if it cannot be read.
*
* @param taskOutput the script task output to read the exit code from
* @return the exit code, or `null` if the exit code is unavailable or an error occurs while reading it
*/
private Integer safeExitCode(ScriptOutput taskOutput) {
try {
return taskOutput.getExitCode();
Expand All @@ -210,6 +252,12 @@ private Integer safeExitCode(ScriptOutput taskOutput) {
}
}

/**
* Retrieve the task's `vars` map, returning null if it cannot be read.
*
* @param taskOutput the script task output to extract variables from
* @return the `vars` map produced by the task, or `null` if unavailable or an error occurs
*/
private Map<String, Object> safeVars(ScriptOutput taskOutput) {
try {
return taskOutput.getVars();
Expand All @@ -220,6 +268,12 @@ private Map<String, Object> safeVars(ScriptOutput taskOutput) {

private record ExtractedFailure(Integer exitCode, String logs) {}

/**
* Extracts the task exit code and available logs from a RunnableTaskException by locating an underlying TaskException.
*
* @param e the RunnableTaskException thrown during task execution
* @return an ExtractedFailure containing the extracted exit code (may be null) and captured logs (may be null)
*/
private ExtractedFailure extractFailure(RunnableTaskException e) {
Integer exitCode = null;
String logs = null;
Expand Down Expand Up @@ -272,4 +326,4 @@ Vars produced by the task (e.g. via ::{"outputs":{...}}:: convention). This is t
)
private String logs;
}
}
}