@@ -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,84 @@ 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+ // and should be skipped for /retest or /ok-to-test commands
171+ func (p * PacRun ) isAlreadyExecuted (ctx context.Context , match matcher.Match ) (bool , error ) {
172+ // apply this check for /retest and /ok-to-test commands
173+ if p .event .EventType != opscomments .RetestAllCommentEventType .String () &&
174+ p .event .EventType != opscomments .OkToTestCommentEventType .String () {
175+ return false , nil
176+ }
177+
178+ // Get existing PipelineRuns for this repository that match the current SHA and PR number
179+ labelSelector := fmt .Sprintf ("%s=%s,%s=%d,%s=%s" ,
180+ apipac .SHA , p .event .SHA ,
181+ apipac .PullRequest , p .event .PullRequestNumber ,
182+ apipac .Repository , match .Repo .Name )
183+
184+ existingPRs , err := p .run .Clients .Tekton .TektonV1 ().PipelineRuns (match .Repo .Namespace ).List (ctx , metav1.ListOptions {
185+ LabelSelector : labelSelector ,
186+ })
187+ if err != nil {
188+ return false , fmt .Errorf ("failed to get existing pipelineruns: %w" , err )
189+ }
190+
191+ // check for any successful runs for this specific pipeline
192+ targetPRName := strings .TrimSuffix (match .PipelineRun .GetGenerateName (), "-" )
193+ if targetPRName == "" {
194+ targetPRName = match .PipelineRun .GetName ()
195+ }
196+
197+ for _ , pr := range existingPRs .Items {
198+ // Skip pipeline runs that are still running or not done
199+ if ! pr .IsDone () || pr .Status .CompletionTime == nil {
200+ continue
201+ }
202+
203+ // if it's the same pipeline
204+ existingPRName := ""
205+ if originalPRName , ok := pr .GetAnnotations ()[apipac .OriginalPRName ]; ok {
206+ existingPRName = originalPRName
207+ } else {
208+ continue
209+ }
210+
211+ // Only skip if the pipeline was successful
212+ if existingPRName == targetPRName && pr .Status .GetCondition ("Succeeded" ).IsTrue () {
213+ msg := fmt .Sprintf ("Skipping pipeline run %s as it has already completed successfully for SHA %s on PR #%d" ,
214+ targetPRName , p .event .SHA , p .event .PullRequestNumber )
215+ p .eventEmitter .EmitMessage (match .Repo , zap .InfoLevel , "RepositorySkippingPipelineRun" , msg )
216+ return true , nil
217+ }
218+ }
219+
220+ return false , nil
221+ }
222+
223+ // filterAlreadyExecutedPipelines filters out pipeline runs that have already been executed successfully
224+ func (p * PacRun ) filterAlreadyExecutedPipelines (ctx context.Context , matchedPRs []matcher.Match ) ([]matcher.Match , error ) {
225+ filteredMatches := []matcher.Match {}
226+ for _ , match := range matchedPRs {
227+ alreadyExecuted , err := p .isAlreadyExecuted (ctx , match )
228+ if err != nil {
229+ prName := match .PipelineRun .GetGenerateName ()
230+ if prName == "" {
231+ prName = match .PipelineRun .GetName ()
232+ }
233+ msg := fmt .Sprintf ("Error checking if pipeline %s was already executed: %v" ,
234+ prName , err )
235+ p .eventEmitter .EmitMessage (match .Repo , zap .WarnLevel , "RepositoryCheckExecution" , msg )
236+ filteredMatches = append (filteredMatches , match )
237+ continue
238+ }
239+
240+ if ! alreadyExecuted {
241+ filteredMatches = append (filteredMatches , match )
242+ }
243+ }
244+ return filteredMatches , nil
245+ }
246+
168247// getPipelineRunsFromRepo fetches pipelineruns from git repository and prepare them for creation.
169248func (p * PacRun ) getPipelineRunsFromRepo (ctx context.Context , repo * v1alpha1.Repository ) ([]matcher.Match , error ) {
170249 provenance := "source"
@@ -262,6 +341,14 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
262341 }
263342 return nil , nil
264343 }
344+
345+ // filter out pipelines that have already been executed successfully
346+ if p .event .EventType == opscomments .RetestAllCommentEventType .String () || p .event .EventType == opscomments .OkToTestCommentEventType .String () {
347+ matchedPRs , err = p .filterAlreadyExecutedPipelines (ctx , matchedPRs )
348+ if err != nil {
349+ return nil , err
350+ }
351+ }
265352 }
266353
267354 // 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