@@ -25,10 +25,10 @@ import (
2525)
2626
2727func (p * PacRun ) matchRepoPR (ctx context.Context ) ([]matcher.Match , * v1alpha1.Repository , error ) {
28- p .debugf ("matchRepoPR: starting repo verification for url=%s" , p .event .URL )
29- repo , err := p .verifyRepoAndUser (ctx )
28+ p .debugf ("matchRepoPR: starting repo setup for url=%s" , p .event .URL )
29+ repo , err := p .setupRepo (ctx )
3030 if err != nil {
31- return nil , nil , err
31+ return nil , repo , err
3232 }
3333 if repo == nil {
3434 p .debugf ("matchRepoPR: no repository match for url=%s" , p .event .URL )
@@ -40,8 +40,24 @@ func (p *PacRun) matchRepoPR(ctx context.Context) ([]matcher.Match, *v1alpha1.Re
4040 return nil , repo , p .cancelPipelineRunsOpsComment (ctx , repo )
4141 }
4242
43+ rawTemplates , err := p .fetchTektonTemplates (ctx , repo )
44+ if err != nil {
45+ return nil , repo , err
46+ }
47+ if rawTemplates == "" {
48+ return nil , repo , nil
49+ }
50+ allowed , err := p .checkUserAccess (ctx , repo )
51+ if err != nil {
52+ return nil , repo , err
53+ }
54+ if ! allowed {
55+ p .debugf ("matchRepoPR: ACL check denied access" )
56+ return nil , repo , nil
57+ }
58+
4359 p .debugf ("matchRepoPR: fetching pipelineruns from repo=%s/%s" , repo .GetNamespace (), repo .GetName ())
44- matchedPRs , err := p .getPipelineRunsFromRepo (ctx , repo )
60+ matchedPRs , err := p .getPipelineRunsFromRepo (ctx , repo , rawTemplates )
4561 if err != nil {
4662 return nil , repo , err
4763 }
@@ -50,11 +66,11 @@ func (p *PacRun) matchRepoPR(ctx context.Context) ([]matcher.Match, *v1alpha1.Re
5066 return matchedPRs , repo , nil
5167}
5268
53- // verifyRepoAndUser verifies if the Repo CR exists for the Git Repository ,
54- // if the user has permission to run CI and also initialise provider client .
55- func (p * PacRun ) verifyRepoAndUser (ctx context.Context ) (* v1alpha1.Repository , error ) {
69+ // setupRepo matches the Repository CR, sets up the authenticated provider client ,
70+ // and fetches commit info. It does NOT perform ACL checks .
71+ func (p * PacRun ) setupRepo (ctx context.Context ) (* v1alpha1.Repository , error ) {
5672 // Match the Event URL to a Repository URL,
57- p .debugf ("verifyRepoAndUser : matching repository for url=%s" , p .event .URL )
73+ p .debugf ("setupRepo : matching repository for url=%s" , p .event .URL )
5874 repo , err := matcher .MatchEventURLRepo (ctx , p .run , p .event , "" )
5975 if err != nil {
6076 return nil , fmt .Errorf ("error matching Repository for event: %w" , err )
@@ -65,13 +81,13 @@ func (p *PacRun) verifyRepoAndUser(ctx context.Context) (*v1alpha1.Repository, e
6581 p .eventEmitter .EmitMessage (nil , zap .WarnLevel , "RepositoryNamespaceMatch" , msg )
6682 return nil , nil
6783 }
68- p .debugf ("verifyRepoAndUser : matched repo=%s/%s" , repo .GetNamespace (), repo .GetName ())
84+ p .debugf ("setupRepo : matched repo=%s/%s" , repo .GetNamespace (), repo .GetName ())
6985
7086 p .logger = p .logger .With ("namespace" , repo .Namespace )
7187 p .vcx .SetLogger (p .logger )
7288 p .eventEmitter .SetLogger (p .logger )
7389
74- // Set up authenticated client with proper token scoping
90+ // Set up authenticated client with proper token scoping.
7591 // NOTE: This is typically already done in sinker.processEvent() for all event types,
7692 // but we call it here as a safety net for edge cases (e.g., tests calling Run() directly,
7793 // or if the early setup in sinker failed/was skipped). The call is idempotent.
@@ -80,21 +96,25 @@ func (p *PacRun) verifyRepoAndUser(ctx context.Context) (*v1alpha1.Repository, e
8096 if err != nil {
8197 return repo , err
8298 }
83- p .debugf ("verifyRepoAndUser : authenticated client setup complete" )
99+ p .debugf ("setupRepo : authenticated client setup complete" )
84100
85101 // Get the SHA commit info, we want to get the URL and commit title
86102 if p .event .SHA == "" || p .event .SHATitle == "" || p .event .SHAURL == "" {
87- p .debugf ("verifyRepoAndUser : fetching commit info" )
103+ p .debugf ("setupRepo : fetching commit info" )
88104 if err = p .vcx .GetCommitInfo (ctx , p .event ); err != nil {
89105 return repo , fmt .Errorf ("could not find commit info: %w" , err )
90106 }
91- p .debugf ("verifyRepoAndUser : commit info loaded sha=%s title=%s" , p .event .SHA , p .event .SHATitle )
107+ p .debugf ("setupRepo : commit info loaded sha=%s title=%s" , p .event .SHA , p .event .SHATitle )
92108 }
93109
110+ return repo , nil
111+ }
112+
113+ func (p * PacRun ) checkUserAccess (ctx context.Context , repo * v1alpha1.Repository ) (bool , error ) {
94114 // Verify whether the sender of the GitOps command (e.g., /test) has the appropriate permissions to
95115 // trigger CI on the repository, as any user is able to comment on a pushed commit in open-source repositories.
96116 if p .event .TriggerTarget == triggertype .Push && opscomments .IsAnyOpsEventType (p .event .EventType ) {
97- p .debugf ("verifyRepoAndUser : checking access for gitops comment on push" )
117+ p .debugf ("checkUserAccess : checking access for gitops comment on push" )
98118 status := providerstatus.StatusOpts {
99119 Status : CompletedStatus ,
100120 Title : "Permission denied" ,
@@ -103,15 +123,15 @@ func (p *PacRun) verifyRepoAndUser(ctx context.Context) (*v1alpha1.Repository, e
103123 AccessDenied : true ,
104124 }
105125 if allowed , err := p .checkAccessOrError (ctx , repo , status , "by GitOps comment on push commit" ); ! allowed {
106- return nil , err
126+ return false , err
107127 }
108128 }
109129
110130 // Check if the submitter is allowed to run this.
111131 // on push we don't need to check the policy since the user has pushed to the repo so it has access to it.
112132 // on comment we skip it for now, we are going to check later on
113133 if p .event .TriggerTarget != triggertype .Push && p .event .EventType != opscomments .NoOpsCommentEventType .String () {
114- p .debugf ("verifyRepoAndUser : checking access for trigger target=%s event_type=%s" , p .event .TriggerTarget , p .event .EventType )
134+ p .debugf ("checkUserAccess : checking access for trigger target=%s event_type=%s" , p .event .TriggerTarget , p .event .EventType )
115135 status := providerstatus.StatusOpts {
116136 Status : queuedStatus ,
117137 Title : "Pending approval, waiting for an /ok-to-test" ,
@@ -120,7 +140,7 @@ func (p *PacRun) verifyRepoAndUser(ctx context.Context) (*v1alpha1.Repository, e
120140 AccessDenied : true ,
121141 }
122142 if allowed , err := p .checkAccessOrError (ctx , repo , status , "via " + p .event .TriggerTarget .String ()); ! allowed {
123- return nil , err
143+ return false , err
124144 }
125145 // When /ok-to-test is approved, update the parent "Pipelines as Code CI" status to success
126146 // to indicate the approval was successful before pipelines start running.
@@ -136,16 +156,15 @@ func (p *PacRun) verifyRepoAndUser(ctx context.Context) (*v1alpha1.Repository, e
136156 }
137157 }
138158 }
139- return repo , nil
159+ return true , nil
140160}
141161
142- // getPipelineRunsFromRepo fetches pipelineruns from git repository and prepare them for creation.
143- func (p * PacRun ) getPipelineRunsFromRepo (ctx context.Context , repo * v1alpha1.Repository ) ([]matcher.Match , error ) {
162+ func (p * PacRun ) fetchTektonTemplates (ctx context.Context , repo * v1alpha1.Repository ) (string , error ) {
144163 provenance := "source"
145164 if repo .Spec .Settings != nil && repo .Spec .Settings .PipelineRunProvenance != "" {
146165 provenance = repo .Spec .Settings .PipelineRunProvenance
147166 }
148- p .debugf ("getPipelineRunsFromRepo : repo=%s/%s provenance=%s" , repo .GetNamespace (), repo .GetName (), provenance )
167+ p .debugf ("fetchTektonTemplates : repo=%s/%s provenance=%s" , repo .GetNamespace (), repo .GetName (), provenance )
149168 rawTemplates , err := p .vcx .GetTektonDir (ctx , p .event , tektonDir , provenance )
150169 if err != nil && p .event .TriggerTarget == triggertype .PullRequest && strings .Contains (err .Error (), "error unmarshalling yaml file" ) {
151170 // make the error a bit more friendly for users who don't know what marshalling or intricacies of the yaml parser works
@@ -163,43 +182,62 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
163182 },
164183 },
165184 )
166- return nil , nil
185+ return "" , nil
167186 }
168187
169- return nil , err
188+ return "" , err
170189 }
171190
172191 if err != nil {
173- p .debugf ("getPipelineRunsFromRepo: GetTektonDir returned error: %v" , err )
174- } else {
175- p .debugf ("getPipelineRunsFromRepo: fetched templates length=%d" , len (rawTemplates ))
176- }
177-
178- if rawTemplates == "" && p .event .EventType == opscomments .OkToTestCommentEventType .String () {
179- err = p .createNeutralStatus (ctx , ".tekton directory not found" , tektonDirMissingError )
180- if err != nil {
181- p .eventEmitter .EmitMessage (nil , zap .ErrorLevel , "RepositoryCreateStatus" , err .Error ())
192+ p .debugf ("fetchTektonTemplates: GetTektonDir returned error: %v" , err )
193+ msg := ""
194+ if strings .Contains (err .Error (), "error unmarshalling yaml file" ) {
195+ msg = "PipelineRun YAML validation"
182196 }
197+ msg += fmt .Sprintf (" err: %s" , err .Error ())
198+ p .eventEmitter .EmitMessage (nil , zap .ErrorLevel , "RepositoryInvalidPipelineRunTemplate" , msg )
199+ return "" , nil
183200 }
184201
185- // This is for push event error logging because we can't create comment for yaml validation errors on push
186- if err != nil || rawTemplates == "" {
187- msg := ""
188- reason := "RepositoryPipelineRunNotFound"
189- logLevel := zap .InfoLevel
190- if err != nil {
191- reason = "RepositoryInvalidPipelineRunTemplate"
192- logLevel = zap .ErrorLevel
193- if strings .Contains (err .Error (), "error unmarshalling yaml file" ) {
194- msg = "PipelineRun YAML validation"
202+ p .debugf ("fetchTektonTemplates: fetched templates length=%d" , len (rawTemplates ))
203+
204+ if rawTemplates == "" {
205+ if p .event .EventType == opscomments .OkToTestCommentEventType .String () {
206+ if statusErr := p .createNeutralStatus (ctx , ".tekton directory not found" , tektonDirMissingError ); statusErr != nil {
207+ p .eventEmitter .EmitMessage (nil , zap .ErrorLevel , "RepositoryCreateStatus" , statusErr .Error ())
195208 }
196- msg += fmt .Sprintf (" err: %s" , err .Error ())
197- } else {
198- msg = fmt .Sprintf ("cannot locate templates in %s/ directory for this repository in %s" , tektonDir , p .event .HeadBranch )
199209 }
200- p .eventEmitter .EmitMessage (nil , logLevel , reason , msg )
210+ msg := fmt .Sprintf ("cannot locate templates in %s/ directory for this repository in %s" , tektonDir , p .event .HeadBranch )
211+ p .eventEmitter .EmitMessage (nil , zap .InfoLevel , "RepositoryPipelineRunNotFound" , msg )
212+ return "" , nil
213+ }
214+
215+ return rawTemplates , nil
216+ }
217+
218+ // verifyRepoAndUser verifies if the Repo CR exists for the Git Repository,
219+ // if the user has permission to run CI and also initialise provider client.
220+ func (p * PacRun ) verifyRepoAndUser (ctx context.Context ) (* v1alpha1.Repository , error ) {
221+ repo , err := p .setupRepo (ctx )
222+ if err != nil {
223+ return repo , err
224+ }
225+ if repo == nil {
201226 return nil , nil
202227 }
228+ allowed , err := p .checkUserAccess (ctx , repo )
229+ if err != nil {
230+ return nil , err
231+ }
232+ if ! allowed {
233+ return nil , nil
234+ }
235+ return repo , nil
236+ }
237+
238+ // getPipelineRunsFromRepo processes pre-fetched .tekton templates and prepares PipelineRuns for creation.
239+ func (p * PacRun ) getPipelineRunsFromRepo (ctx context.Context , repo * v1alpha1.Repository , rawTemplates string ) ([]matcher.Match , error ) {
240+ p .debugf ("getPipelineRunsFromRepo: repo=%s/%s templates_length=%d" , repo .GetNamespace (), repo .GetName (), len (rawTemplates ))
203241
204242 // check for condition if need update the pipelinerun with regexp from the
205243 // "raw" pipelinerun string
0 commit comments