Repository: tektoncd/pipeline (language: go)

Issue:
task results not replaced with their values in childReferences
# Expected Behavior

As a user, I expect the `pipelineRun` status to include the results in place of a task result reference. This helps diagnose the pipeline run using its status.

# Actual Behavior

`pipelineRun.status.childReferences` does not replace a task result when referred in `when` expressions using `"$(tasks.task1.results.string-result)"` or `"$(tasks.task1.results.array-results[1])"`.

# Steps to Reproduce the Problem

1. Deploy Tekton Pipelines with the latest commit
2. Enable `alpha` feature flag 

```
kubectl patch cm/feature-flags -p "{\"data\":{\"enable-api-fields\":\"alpha\"}}" -n tekton-pipelines
```

3. Apply pipelineRun to the cluster.

```
kubectl create -f pipeline-with-when-expressions-using-array-results.yaml
pipelinerun.tekton.dev/pipelinerun-with-when-expressions-using-array-results-dcghl created
```

```
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  generateName: pipelinerun-with-when-expressions-using-array-results-
spec:
  pipelineSpec:
    tasks:
      - name: task1
        taskSpec:
          results:
            - name: array-results
              type: array
              description: The array results
            - name: string-result
              type: string
              description: The string results
          steps:
            - name: write-array
              image: bash:latest
              script: |
                #!/usr/bin/env bash
                echo -n "[\"1\", \"2\", \"3\"]" | tee $(results.array-results.path)
                echo -n "1" | tee $(results.string-result.path)
      - name: task2
        when:
          - input: "$(tasks.task1.results.array-results[1])"
            operator: in
            values: ["2"]
          - input: "$(tasks.task1.results.string-result)"
            operator: in
            values: ["1"]
        taskSpec:
          steps:
            - name: say-hi
              image: bash:latest
              script: |
                echo "hi"
```

4. Check pipelineRun status:

```
 kubectl get pr pipelinerun-with-when-expressions-using-array-results-w74qq -o json | jq .status.childReferences
[
  {
    "apiVersion": "tekton.dev/v1beta1",
    "kind": "TaskRun",
    "name": "pipelinerun-with-when-exp9fe3c5278f6960248e739a1c08e9e593-task1",
    "pipelineTaskName": "task1"
  },
  {
    "apiVersion": "tekton.dev/v1beta1",
    "kind": "TaskRun",
    "name": "pipelinerun-with-when-exp9fe3c5278f6960248e739a1c08e9e593-task2",
    "pipelineTaskName": "task2",
    "whenExpressions": [
      {
        "input": "$(tasks.task1.results.array-results[1])",
        "operator": "in",
        "values": [
          "2"
        ]
      },
      {
        "input": "$(tasks.task1.results.string-result)",
        "operator": "in",
        "values": [
          "1"
        ]
      }
    ]
  }
]
```

# Additional Info

- Kubernetes version:

  **Output of `kubectl version`:**

```
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.14", GitCommit:"3321ffc07d2f046afdf613796f9032f4460de093", GitTreeState:"clean", BuildDate:"2022-11-09T13:40:19Z", GoVersion:"go1.17.13", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.0", GitCommit:"ab69524f795c42094a6630298ff53f3c3ebab7f4", GitTreeState:"clean", BuildDate:"2021-12-07T18:09:57Z", GoVersion:"go1.17.3", Compiler:"gc", Platform:"linux/amd64"}
```

- Tekton Pipeline version:

  **Output of `tkn version` or `kubectl get pods -n tekton-pipelines -l app=tekton-pipelines-controller -o=jsonpath='{.items[0].metadata.labels.version}'`**

```
Client version: 0.23.1
Pipeline version: v0.43.0
```


<!-- Any other additional information -->



Discussion / hints:
is this also part of tep-75&76 promotion?
This seems to be a bug before tep 75&76, but worth investigating 
@pritidesai Just to confirm, is it expected for `when` conditions to be included in `childReferences` ?
> is this also part of tep-75&76 promotion?

Hey @Yongxuanzhang this is not blocking TEP-0075 and TEP-0076 promotion, sorry for the confusion. Like its mentioned in the description, the values are not replaced even for the results which are of type `string`.
> @pritidesai Just to confirm, is it expected for `when` conditions to be included in `childReferences` ?

Thanks @afrittoli, yes `when` expressions are part of the `childReferences`:

https://github.com/tektoncd/pipeline/blob/bd6a12fb6f3f95d5435b688a46d1b66f50f063c5/pkg/apis/pipeline/v1beta1/pipelinerun_types.go#L401:L413

It's valuable for the user to have this information as part of the pipelineRun `status` because `when` expression is a pipeline component and associated with a `pipelineTask`. That's the reason, `ChildReferences` is defined to have a name of the `taskRun`, name of the `pipelineTask`, and the `when` expressions.

Fix patch:
diff --git a/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go
index 899ab6346dc..3d52dc882c3 100644
--- a/pkg/reconciler/pipelinerun/pipelinerun.go
+++ b/pkg/reconciler/pipelinerun/pipelinerun.go
@@ -697,7 +697,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
 	after = pr.Status.GetCondition(apis.ConditionSucceeded)
 	pr.Status.StartTime = pipelineRunFacts.State.AdjustStartTime(pr.Status.StartTime)
 
-	pr.Status.ChildReferences = pipelineRunFacts.State.GetChildReferences()
+	pr.Status.ChildReferences = pipelineRunFacts.GetChildReferences()
 
 	pr.Status.SkippedTasks = pipelineRunFacts.GetSkippedTasks()
 	if after.Status == corev1.ConditionTrue || after.Status == corev1.ConditionFalse {
diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go b/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go
index 1c8e9038b28..65882f4c987 100644
--- a/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go
+++ b/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go
@@ -188,10 +188,18 @@ func (state PipelineRunState) GetRunsResults() map[string][]v1beta1.CustomRunRes
 
 // GetChildReferences returns a slice of references, including version, kind, name, and pipeline task name, for all
 // TaskRuns and Runs in the state.
-func (state PipelineRunState) GetChildReferences() []v1.ChildStatusReference {
+func (facts *PipelineRunFacts) GetChildReferences() []v1.ChildStatusReference {
 	var childRefs []v1.ChildStatusReference
 
-	for _, rpt := range state {
+	for _, rpt := range facts.State {
+		// try to replace the parameters of the reference result of whenexpression in the taskrun that has ended
+		if rpt.isDone(facts) {
+			resolvedResultRefs, _, err := ResolveResultRefs(facts.State, PipelineRunState{rpt})
+			if err == nil {
+				ApplyTaskResults(facts.State, resolvedResultRefs)
+			}
+		}
+
 		switch {
 		case len(rpt.TaskRuns) != 0:
 			for _, taskRun := range rpt.TaskRuns {


Test patch:
diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go b/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go
index d5e4b918e96..918337be4ae 100644
--- a/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go
+++ b/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go
@@ -3058,7 +3058,7 @@ func TestPipelineRunState_GetChildReferences(t *testing.T) {
 							Value: v1.ParamValue{Type: v1.ParamTypeArray, ArrayVal: []string{"qux", "baz"}},
 						}}},
 				},
-				TaskRuns: []*v1.TaskRun{nil, nil, nil, nil},
+				TaskRuns: []*v1.TaskRun{},
 			}},
 			childRefs: nil,
 		},
@@ -3259,10 +3259,127 @@ func TestPipelineRunState_GetChildReferences(t *testing.T) {
 				}},
 			}},
 		},
+		{
+			name: "whenexpressions-reference-task-results",
+			state: PipelineRunState{
+				{
+					TaskRunNames: []string{"task-generate-results"},
+					PipelineTask: &v1.PipelineTask{
+						Name: "task1",
+					},
+					TaskRuns: []*v1.TaskRun{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name: "task-generate-results",
+							},
+							Status: v1.TaskRunStatus{
+								Status: duckv1.Status{Conditions: []apis.Condition{{
+									Type:   apis.ConditionSucceeded,
+									Status: corev1.ConditionTrue,
+								}}},
+								TaskRunStatusFields: v1.TaskRunStatusFields{
+									Results: []v1.TaskRunResult{
+										{
+											Name: "string-result",
+											Value: v1.ResultValue{
+												StringVal: "value1",
+												Type:      v1.ParamTypeString,
+											},
+										},
+										{
+											Name: "array-result",
+											Value: v1.ResultValue{
+												ArrayVal: []string{
+													"array-value1",
+													"array-value2",
+												},
+												Type: v1.ParamTypeArray,
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				{
+					TaskRunNames: []string{"task-reference-previous-result"},
+					PipelineTask: &v1.PipelineTask{
+						Name: "task2",
+						When: []v1.WhenExpression{
+							{
+								Input:    "$(tasks.task1.results.string-result)",
+								Operator: selection.In,
+								Values:   []string{"value1"},
+							},
+							{
+								Input:    "$(tasks.task1.results.array-result[1])",
+								Operator: selection.In,
+								Values:   []string{"array-value2"},
+							},
+						},
+					},
+					TaskRuns: []*v1.TaskRun{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name: "task-reference-previous-result",
+							},
+							Status: v1.TaskRunStatus{
+								Status: duckv1.Status{Conditions: []apis.Condition{{
+									Type:   apis.ConditionSucceeded,
+									Status: corev1.ConditionFalse,
+								}}},
+							},
+						},
+					},
+				},
+			},
+			childRefs: []v1.ChildStatusReference{
+				{
+					TypeMeta: runtime.TypeMeta{
+						APIVersion: "tekton.dev/v1",
+						Kind:       "TaskRun",
+					},
+					Name:             "task-generate-results",
+					PipelineTaskName: "task1",
+				},
+				{
+					TypeMeta: runtime.TypeMeta{
+						APIVersion: "tekton.dev/v1",
+						Kind:       "TaskRun",
+					},
+					Name:             "task-reference-previous-result",
+					PipelineTaskName: "task2",
+					WhenExpressions: []v1.WhenExpression{
+						{
+							Input:    "value1",
+							Operator: selection.In,
+							Values:   []string{"value1"},
+						},
+						{
+							Input:    "array-value2",
+							Operator: selection.In,
+							Values:   []string{"array-value2"},
+						},
+					},
+				},
+			},
+		},
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			childRefs := tc.state.GetChildReferences()
+			d, err := dagFromState(tc.state)
+			if err != nil {
+				t.Fatalf("Unexpected error while building DAG for state %v: %v", tc.state, err)
+			}
+			childRefs := (&PipelineRunFacts{
+				State:           tc.state,
+				TasksGraph:      d,
+				FinalTasksGraph: &dag.Graph{},
+				TimeoutsState: PipelineRunTimeoutsState{
+					Clock: testClock,
+				},
+			}).GetChildReferences()
 			if d := cmp.Diff(tc.childRefs, childRefs); d != "" {
 				t.Errorf("Didn't get expected child references for %s: %s", tc.name, diff.PrintWantGot(d))
 			}