@@ -12,6 +12,8 @@ import (
1212 "strconv"
1313 "strings"
1414
15+ "github.com/openshift-pipelines/pipelines-as-code/pkg/action"
16+ "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
1517 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
1618 "github.com/openshift-pipelines/pipelines-as-code/pkg/changedfiles"
1719 "github.com/openshift-pipelines/pipelines-as-code/pkg/events"
@@ -354,22 +356,42 @@ func (v *Provider) CreateStatus(ctx context.Context, event *info.Event, statusOp
354356 Context : gitlab .Ptr (contextName ),
355357 }
356358
359+ // Read a previously stored pipeline ID from PipelineRun annotations so
360+ // that all commit statuses land in the same GitLab pipeline instead of
361+ // GitLab potentially auto-creating a new "external" pipeline mid-stream.
362+ if statusOpts .PipelineRun != nil {
363+ if id , ok := statusOpts .PipelineRun .GetAnnotations ()[keys .GitLabPipelineID ]; ok {
364+ pid , err := strconv .ParseInt (id , 10 , 64 )
365+ if err == nil {
366+ opt .PipelineID = gitlab .Ptr (pid )
367+ }
368+ }
369+ }
370+
357371 // In case we have access, set the status. Typically, on a Merge Request (MR)
358372 // from a fork in an upstream repository, the token needs to have write access
359373 // to the fork repository in order to create a status. However, the token set on the
360374 // Repository CR usually doesn't have such broad access, preventing from creating
361375 // a status comment on it.
362376 // This would work on a push or an MR from a branch within the same repo.
363377 // Ignoring errors because of the write access issues,
364- _ , _ , err := v .Client ().Commits .SetCommitStatus (event .SourceProjectID , event .SHA , opt )
378+ commitStatus , _ , err := v .Client ().Commits .SetCommitStatus (event .SourceProjectID , event .SHA , opt )
365379 if err != nil {
366380 v .Logger .Debugf ("cannot set status with the GitLab token on the source project: %v" , err )
367381 } else {
382+ v .patchPipelineIDAnnotation (ctx , statusOpts , commitStatus )
368383 // we managed to set the status on the source repo, all good we are done
369384 v .Logger .Debugf ("created commit status on source project ID %d" , event .TargetProjectID )
370385 return nil
371386 }
372- if _ , _ , err = v .Client ().Commits .SetCommitStatus (event .TargetProjectID , event .SHA , opt ); err == nil {
387+ // Clear pipeline ID when falling back to the target project — the cached
388+ // ID belongs to the source project's pipeline namespace and is invalid on
389+ // a different project (fork MR scenario).
390+ if event .SourceProjectID != event .TargetProjectID {
391+ opt .PipelineID = nil
392+ }
393+ if commitStatus , _ , err = v .Client ().Commits .SetCommitStatus (event .TargetProjectID , event .SHA , opt ); err == nil {
394+ v .patchPipelineIDAnnotation (ctx , statusOpts , commitStatus )
373395 v .Logger .Debugf ("created commit status on target project ID %d" , event .TargetProjectID )
374396 // we managed to set the status on the target repo, all good we are done
375397 return nil
@@ -860,3 +882,32 @@ func (v *Provider) formatPipelineComment(sha string, status providerstatus.Statu
860882 return fmt .Sprintf ("%s **%s: %s/%s for %s**\n \n %s\n \n <small>Full log available [here](%s)</small>" ,
861883 emoji , status .Title , v .pacInfo .ApplicationName , status .OriginalPipelineRunName , sha , status .Text , status .DetailsURL )
862884}
885+
886+ // patchPipelineIDAnnotation stores the GitLab pipeline ID from a successful
887+ // SetCommitStatus response as a PipelineRun annotation. This allows the
888+ // reconciler (which creates a new Provider instance) to read it back and
889+ // pass it on subsequent status updates, keeping all statuses in the same
890+ // GitLab pipeline.
891+ func (v * Provider ) patchPipelineIDAnnotation (ctx context.Context , statusOpts providerstatus.StatusOpts , cs * gitlab.CommitStatus ) {
892+ if cs == nil || cs .PipelineID == 0 {
893+ return
894+ }
895+ pr := statusOpts .PipelineRun
896+ if pr == nil || (pr .GetName () == "" && pr .GetGenerateName () == "" ) {
897+ return
898+ }
899+ // Skip if annotation is already set — avoid unnecessary patches.
900+ if _ , ok := pr .GetAnnotations ()[keys .GitLabPipelineID ]; ok {
901+ return
902+ }
903+ mergePatch := map [string ]any {
904+ "metadata" : map [string ]any {
905+ "annotations" : map [string ]string {
906+ keys .GitLabPipelineID : strconv .FormatInt (cs .PipelineID , 10 ),
907+ },
908+ },
909+ }
910+ if _ , err := action .PatchPipelineRun (ctx , v .Logger , "gitlabPipelineID" , v .run .Clients .Tekton , pr , mergePatch ); err != nil {
911+ v .Logger .Debugf ("failed to patch pipelinerun with gitlab pipeline ID: %v" , err )
912+ }
913+ }
0 commit comments