Skip to content

Commit 0910541

Browse files
BoseKarthikeyanBose
authored andcommitted
fix(llm): run default AI roles on completed PipelineRuns
1 parent 74939ef commit 0910541

4 files changed

Lines changed: 45 additions & 12 deletions

File tree

docs/content/docs/guides/llm-analysis/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Each role defines a specific analysis scenario. You can configure multiple roles
9393
| `name` | string | Yes | Unique identifier for this role |
9494
| `prompt` | string | Yes | Prompt template for the LLM |
9595
| `model` | string | No | Model name (consult provider documentation for available models). Uses provider default if not specified. |
96-
| `on_cel` | string | No | CEL expression for conditional triggering. If not specified, the role runs only for failed PipelineRuns. |
96+
| `on_cel` | string | No | CEL expression for conditional triggering. If not specified, the role runs for completed PipelineRuns (both successful and failed). |
9797
| `output` | string | Yes | Output destination (currently only `pr-comment` is supported) |
9898
| `context_items` | object | No | Configuration for context inclusion |
9999

docs/content/docs/guides/llm-analysis/model-and-triggers.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,19 @@ settings:
5050
5151
## CEL Expressions for Triggers
5252
53-
By default, Pipelines-as-Code runs LLM analysis only for failed PipelineRuns. CEL (Common Expression Language) expressions in the `on_cel` field let you refine this behavior -- for example, restricting analysis to a specific branch or enabling it for successful runs too.
53+
By default, Pipelines-as-Code runs LLM analysis for completed PipelineRuns (both successful and failed). CEL (Common Expression Language) expressions in the `on_cel` field let you refine this behavior -- for example, restricting analysis to a specific branch, only successful runs, or only failed runs.
5454

55-
If you omit `on_cel`, the role executes for all failed PipelineRuns.
55+
If you omit `on_cel`, the role executes for all completed PipelineRuns.
5656

5757
### Overriding the Default Behavior
5858

59-
To run analysis for **all PipelineRuns** (both successful and failed), set `on_cel: 'true'`:
59+
To run analysis for **all completed PipelineRuns** (both successful and failed), set `on_cel: 'true'`:
6060
6161
```yaml
6262
roles:
6363
- name: "pipeline-summary"
6464
prompt: "Generate a summary of this pipeline run..."
65-
on_cel: 'true' # Runs for ALL pipeline runs, not just failures
65+
on_cel: 'true' # Runs for all completed pipeline runs
6666
output: "pr-comment"
6767
```
6868
@@ -76,13 +76,13 @@ This is useful when you want to:
7676
### Example CEL Expressions
7777
7878
```yaml
79-
# Run on ALL pipeline runs (overrides default failed-only behavior)
79+
# Run on ALL pipeline runs (overrides default completed-only behavior)
8080
on_cel: 'true'
8181

8282
# Only on successful runs (e.g., for generating success reports)
8383
on_cel: 'body.pipelineRun.status.conditions[0].reason == "Succeeded"'
8484

85-
# Only on pull requests (in addition to default failed-only check)
85+
# Only on pull requests
8686
on_cel: 'body.event.event_type == "pull_request"'
8787

8888
# Only on main branch

pkg/llm/analyzer.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,10 @@ func countFailedResults(results []AnalysisResult) int {
344344
}
345345

346346
// shouldTriggerRole evaluates the CEL expression to determine if a role should be triggered.
347-
// If no on_cel is provided, defaults to triggering only for failed PipelineRuns.
347+
// If no on_cel is provided, the role runs only after the PipelineRun completes.
348348
func shouldTriggerRole(role v1alpha1.AnalysisRole, celContext map[string]any, pr *tektonv1.PipelineRun) (bool, error) {
349349
if role.OnCEL == "" {
350-
succeededCondition := pr.Status.GetCondition(apis.ConditionSucceeded)
351-
return succeededCondition != nil && succeededCondition.Status == corev1.ConditionFalse, nil
350+
return isCompletedPipelineRun(pr), nil
352351
}
353352

354353
result, err := cel.Value(role.OnCEL, celContext["body"],
@@ -366,6 +365,16 @@ func shouldTriggerRole(role v1alpha1.AnalysisRole, celContext map[string]any, pr
366365
return false, fmt.Errorf("CEL expression '%s' did not return boolean value", role.OnCEL)
367366
}
368367

368+
// isCompletedPipelineRun checks whether a PipelineRun has finished,
369+
// regardless of success or failure.
370+
func isCompletedPipelineRun(pr *tektonv1.PipelineRun) bool {
371+
if pr == nil {
372+
return false
373+
}
374+
c := pr.Status.GetCondition(apis.ConditionSucceeded)
375+
return c != nil && (c.Status == corev1.ConditionTrue || c.Status == corev1.ConditionFalse)
376+
}
377+
369378
// validateAnalysisConfig validates the AI analysis configuration.
370379
func validateAnalysisConfig(config *v1alpha1.AIAnalysisConfig) error {
371380
if config.Provider == "" {

pkg/llm/analyzer_test.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ func TestShouldTriggerRoleEvaluations(t *testing.T) {
380380
wantError bool
381381
}{
382382
{
383-
name: "no expression defaults to failed pipelines only",
383+
name: "no expression defaults to completed pipelines",
384384
role: v1alpha1.AnalysisRole{},
385385
want: true,
386386
},
@@ -423,6 +423,9 @@ func TestShouldTriggerRole(t *testing.T) {
423423
succeededPR := &tektonv1.PipelineRun{}
424424
succeededPR.Status.Conditions = append(succeededPR.Status.Conditions, apis.Condition{Type: apis.ConditionSucceeded, Status: "True"})
425425

426+
pendingPR := &tektonv1.PipelineRun{}
427+
pendingPR.Status.Conditions = append(pendingPR.Status.Conditions, apis.Condition{Type: apis.ConditionSucceeded, Status: "Unknown"})
428+
426429
tests := []struct {
427430
name string
428431
role v1alpha1.AnalysisRole
@@ -439,10 +442,31 @@ func TestShouldTriggerRole(t *testing.T) {
439442
wantTrigger: true,
440443
},
441444
{
442-
name: "no cel expression - skips succeeded pipeline",
445+
name: "no cel expression - triggers for succeeded pipeline",
443446
role: v1alpha1.AnalysisRole{Name: "test-role"},
444447
celContext: map[string]any{},
445448
pr: succeededPR,
449+
wantTrigger: true,
450+
},
451+
{
452+
name: "no cel expression - skips pending pipeline",
453+
role: v1alpha1.AnalysisRole{Name: "test-role"},
454+
celContext: map[string]any{},
455+
pr: pendingPR,
456+
wantTrigger: false,
457+
},
458+
{
459+
name: "no cel expression - skips nil pipelinerun",
460+
role: v1alpha1.AnalysisRole{Name: "test-role"},
461+
celContext: map[string]any{},
462+
pr: nil,
463+
wantTrigger: false,
464+
},
465+
{
466+
name: "no cel expression - skips pipelinerun without status",
467+
role: v1alpha1.AnalysisRole{Name: "test-role"},
468+
celContext: nil,
469+
pr: &tektonv1.PipelineRun{},
446470
wantTrigger: false,
447471
},
448472
{

0 commit comments

Comments
 (0)