1919
2020# Filename of the precommit configuration. The client expects this config file at the root of the repository.
2121PRECOMMIT_CONFIG_FILENAME = '.teamscale-precommit.config'
22+ DEFAULT_PROJECT_SUBPATH = ''
2223DEFAULT_PATH_PREFIX = ''
2324
2425
@@ -27,17 +28,20 @@ class PrecommitClient:
2728 # Number of seconds the client waits until fetching precommit results from the server.
2829 PRECOMMIT_WAITING_TIME_IN_SECONDS = 2
2930
30- def __init__ (self , teamscale_config , repository_path , path_prefix = DEFAULT_PATH_PREFIX , analyzed_file = None ,
31- verify = True ,
32- omit_links_to_findings = False , exclude_findings_in_changed_code = False , fetch_existing_findings = False ,
33- fetch_all_findings = False , fetch_existing_findings_in_changes = False , fail_on_red_findings = False ,
34- log_to_stderr = False ):
31+ def __init__ (self , teamscale_config , repository_path , path_prefix = DEFAULT_PATH_PREFIX , project_subpath = '' ,
32+ analyzed_file = None ,
33+ verify = True , omit_links_to_findings = False , exclude_findings_in_changed_code = False ,
34+ fetch_existing_findings = False , fetch_all_findings = False , fetch_existing_findings_in_changes = False ,
35+ fail_on_red_findings = False , log_to_stderr = False ):
3536 """Constructor"""
3637 self .teamscale_client = TeamscaleClient (teamscale_config .url , teamscale_config .username ,
3738 teamscale_config .access_token , teamscale_config .project_id , verify )
3839 self .repository_path = repository_path
40+
3941 # calling os.path.join ensures a tailing '/'
4042 self .path_prefix = os .path .join (path_prefix , '' )
43+ self .project_subpath = os .path .join (project_subpath , '' )
44+
4145 self .analyzed_file = analyzed_file
4246 self .omit_links_to_findings = omit_links_to_findings
4347 self .exclude_findings_in_changed_code = exclude_findings_in_changed_code
@@ -110,21 +114,36 @@ def _upload_precommit_data(self):
110114 self .teamscale_client .branch = self .current_branch
111115
112116 print ("Uploading changes on branch '%s' in '%s'..." % (self .current_branch , self .repository_path ))
113- changed_files_with_path_prefix = self ._apply_path_prefix_to_changed_files ()
114- deleted_files_with_path_prefix = self ._apply_path_prefix_to_deleted_files ()
117+
118+ changed_files_in_project = self ._filter_changed_files_in_project_subpath (self .changed_files )
119+ deleted_files_in_project = self ._filter_deleted_files_in_project_subpath (self .deleted_files )
120+
121+ changed_files_with_path_prefix = self ._apply_path_prefix_to_changed_files (changed_files_in_project )
122+ deleted_files_with_path_prefix = self ._apply_path_prefix_to_deleted_files (deleted_files_in_project )
123+
115124 precommit_data = PreCommitUploadData (uniformPathToContentMap = changed_files_with_path_prefix ,
116125 deletedUniformPaths = deleted_files_with_path_prefix )
117126 self .teamscale_client .upload_files_for_precommit_analysis (
118127 datetime .datetime .fromtimestamp (self .parent_commit_timestamp ), precommit_data )
119128
120- def _apply_path_prefix_to_changed_files (self ):
129+ def _filter_changed_files_in_project_subpath (self , uniform_path_content_map ):
130+ project_files_map = {}
131+ for key in uniform_path_content_map .keys ():
132+ if key .startswith (self .project_subpath ):
133+ project_files_map [key ] = uniform_path_content_map [key ]
134+ return project_files_map
135+
136+ def _filter_deleted_files_in_project_subpath (self , deleted_files ):
137+ return list (filter (lambda path : path .startswith (self .project_subpath ), deleted_files ))
138+
139+ def _apply_path_prefix_to_changed_files (self , changed_files ):
121140 map_with_prefixes = {}
122- for key in self . changed_files .keys ():
123- map_with_prefixes [self .path_prefix + key ] = self . changed_files [key ]
141+ for key in changed_files .keys ():
142+ map_with_prefixes [self .path_prefix + key ] = changed_files [key ]
124143 return map_with_prefixes
125144
126- def _apply_path_prefix_to_deleted_files (self ):
127- return list (map (lambda path : self .path_prefix + path , self . deleted_files ))
145+ def _apply_path_prefix_to_deleted_files (self , deleted_files ):
146+ return list (map (lambda path : self .path_prefix + path , deleted_files ))
128147
129148 def _wait_and_get_precommit_result (self ):
130149 """Gets the current precommit results. Waits synchronously until server is ready. """
@@ -143,9 +162,18 @@ def _print_findings(self, message, findings, branch):
143162
144163 self ._print ('' , log_to_stderr )
145164 self ._print (message , log_to_stderr )
146- for formatted_finding in self ._format_findings (findings , branch ):
165+
166+ findings_without_path_prefix = list (
167+ map (lambda finding : self ._copy_finding_without_path_prefix (finding ), findings ))
168+
169+ findings_in_project = self ._remove_findings_outside_project_subpath (findings_without_path_prefix )
170+
171+ for formatted_finding in self ._format_findings (findings_in_project , branch ):
147172 self ._print (formatted_finding , log_to_stderr )
148173
174+ def _remove_findings_outside_project_subpath (self , findings ):
175+ return list (filter (lambda finding : finding .uniformPath .startswith (self .project_subpath ), findings ))
176+
149177 @staticmethod
150178 def _print (message , print_to_err = False ):
151179 if print_to_err :
@@ -199,9 +227,7 @@ def _format_findings(self, findings, branch):
199227 if len (findings ) == 0 :
200228 return ['> No findings.' ]
201229
202- findings_without_path_prefix = list (
203- map (lambda finding : self ._copy_finding_without_path_prefix (finding ), findings ))
204- sorted_findings = sorted (findings_without_path_prefix )
230+ sorted_findings = sorted (findings )
205231 return [self ._format_message (finding ) for finding in sorted_findings ]
206232
207233 def _format_message (self , finding ):
@@ -271,6 +297,9 @@ def _parse_args():
271297 parser .add_argument ('--log-to-stderr' , dest = 'log_to_stderr' , action = 'store_true' ,
272298 help = 'When this option is set, any finding will be logged to stderr instead of stdout: '
273299 '(default: False)' )
300+ parser .add_argument ('--project-subpath' , metavar = 'project-subpath' , type = str , default = DEFAULT_PROJECT_SUBPATH ,
301+ help = 'Project path relative to the git repository. '
302+ 'Pre-commit analysis will only be performed for files under this path.' )
274303 parser .add_argument ('--path-prefix' , metavar = 'PATH_PREFIX' , type = str ,
275304 help = 'Path prefix on Teamscale as configured with "Prepend repository identifier" or '
276305 '"Path prefix transformation". Please use "/" to separate folders.' ,
@@ -294,7 +323,7 @@ def _configure_precommit_client(parsed_args):
294323 config_file = os .path .join (repo_path , PRECOMMIT_CONFIG_FILENAME )
295324 config = get_teamscale_client_configuration (config_file )
296325 return PrecommitClient (config , repository_path = repo_path , path_prefix = parsed_args .path_prefix ,
297- analyzed_file = path_to_file_in_repo ,
326+ project_subpath = parsed_args . project_subpath , analyzed_file = path_to_file_in_repo ,
298327 verify = parsed_args .verify , omit_links_to_findings = parsed_args .omit_links_to_findings ,
299328 exclude_findings_in_changed_code = parsed_args .exclude_findings_in_changed_code ,
300329 fetch_existing_findings = parsed_args .fetch_existing_findings ,
0 commit comments