@@ -18,6 +18,7 @@ import (
1818 "github.com/openshift-pipelines/pipelines-as-code/pkg/templates"
1919 tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
2020 "go.uber.org/zap"
21+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122)
2223
2324func (p * PacRun ) matchRepoPR (ctx context.Context ) ([]matcher.Match , * v1alpha1.Repository , error ) {
@@ -165,6 +166,77 @@ is that what you want? make sure you use -n when generating the secret, eg: echo
165166 return repo , nil
166167}
167168
169+ // isAlreadyExecuted checks if the pipeline has already been executed for the given SHA and PR number.
170+ func (p * PacRun ) isAlreadyExecuted (ctx context.Context , match matcher.Match ) (bool , error ) {
171+ // Get existing PipelineRuns for this repository that match the current SHA and PR number
172+ labelSelector := fmt .Sprintf ("%s=%s,%s=%d,%s=%s" ,
173+ apipac .SHA , p .event .SHA ,
174+ apipac .PullRequest , p .event .PullRequestNumber ,
175+ apipac .Repository , match .Repo .Name )
176+
177+ existingPRs , err := p .run .Clients .Tekton .TektonV1 ().PipelineRuns (match .Repo .Namespace ).List (ctx , metav1.ListOptions {
178+ LabelSelector : labelSelector ,
179+ })
180+ if err != nil {
181+ return false , fmt .Errorf ("failed to get existing pipelineruns: %w" , err )
182+ }
183+
184+ // check for any successful runs for this specific pipeline
185+ targetPRName := strings .TrimSuffix (match .PipelineRun .GetGenerateName (), "-" )
186+ if targetPRName == "" {
187+ targetPRName = match .PipelineRun .GetName ()
188+ }
189+
190+ for _ , pr := range existingPRs .Items {
191+ // Skip pipeline runs that are still running or not done
192+ if ! pr .IsDone () || pr .Status .CompletionTime == nil {
193+ continue
194+ }
195+
196+ // if it's the same pipeline
197+ existingPRName := ""
198+ if originalPRName , ok := pr .GetAnnotations ()[apipac .OriginalPRName ]; ok {
199+ existingPRName = originalPRName
200+ } else {
201+ continue
202+ }
203+
204+ // Only skip if the pipeline was successful
205+ if existingPRName == targetPRName && pr .Status .GetCondition ("Succeeded" ).IsTrue () {
206+ msg := fmt .Sprintf ("Skipping pipeline run %s as it has already completed successfully for SHA %s on PR #%d" ,
207+ targetPRName , p .event .SHA , p .event .PullRequestNumber )
208+ p .eventEmitter .EmitMessage (match .Repo , zap .InfoLevel , "RepositorySkippingPipelineRun" , msg )
209+ return true , nil
210+ }
211+ }
212+
213+ return false , nil
214+ }
215+
216+ // filterAlreadySuccessfulPipelines filters out pipeline runs that have already been executed successfully.
217+ func (p * PacRun ) filterAlreadySuccessfulPipelines (ctx context.Context , matchedPRs []matcher.Match ) []matcher.Match {
218+ filteredMatches := []matcher.Match {}
219+ for _ , match := range matchedPRs {
220+ alreadyExecuted , err := p .isAlreadyExecuted (ctx , match )
221+ if err != nil {
222+ prName := match .PipelineRun .GetGenerateName ()
223+ if prName == "" {
224+ prName = match .PipelineRun .GetName ()
225+ }
226+ msg := fmt .Sprintf ("Error checking if pipeline %s was already executed: %v" ,
227+ prName , err )
228+ p .eventEmitter .EmitMessage (match .Repo , zap .WarnLevel , "RepositoryCheckExecution" , msg )
229+ filteredMatches = append (filteredMatches , match )
230+ continue
231+ }
232+
233+ if ! alreadyExecuted {
234+ filteredMatches = append (filteredMatches , match )
235+ }
236+ }
237+ return filteredMatches
238+ }
239+
168240// getPipelineRunsFromRepo fetches pipelineruns from git repository and prepare them for creation.
169241func (p * PacRun ) getPipelineRunsFromRepo (ctx context.Context , repo * v1alpha1.Repository ) ([]matcher.Match , error ) {
170242 provenance := "source"
@@ -262,6 +334,11 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
262334 }
263335 return nil , nil
264336 }
337+
338+ // filter out pipelines that have already been executed successfully
339+ if p .event .EventType == opscomments .RetestAllCommentEventType .String () || p .event .EventType == opscomments .OkToTestCommentEventType .String () {
340+ matchedPRs = p .filterAlreadySuccessfulPipelines (ctx , matchedPRs )
341+ }
265342 }
266343
267344 // if the event is a comment event, but we don't have any match from the keys.OnComment then do the ACL checks again
0 commit comments