ATLAS Offline Software
Loading...
Searching...
No Matches
python.update_ci_reference_files Namespace Reference

Classes

class  CITest

Functions

 process_log_file (url, branch, test_name)
 process_diffpool_change (text, ami_tag, mr_number, human_readable_date, test_name)
 process_digest_change (text, ami_tag, mr_number, human_readable_date, test_name)
 update_reference_files (actually_update=True, update_local_files=False)
 create_dir_and_copy_refs (test, actually_update=False)
 process_CI_Tests_json (data)
 strip_url (href)
 strip_href (href)
 process_CI_Builds_Summary (project)
 extract_links_from_json (url)
 handle_shared_refs ()
 summarise_failing_tests (check_for_duplicates=True)

Variables

 failing_tests = defaultdict(list)
list dirs_created = []
bool debug = False
 parser
 help
 action
 args = parser.parse_args()
 mr_url = summarise_failing_tests(not args.test_run)
 gl_project = gitlab.Gitlab("https://gitlab.cern.ch").projects.get("atlas/athena")
 mr = gl_project.mergerequests.get(mr_url.split('/')[-1])
 author = mr.author['username']
str remote = f'https://:@gitlab.cern.ch:8443/{author}/athena.git'
str local_branch = f'mr-{mr.iid}'
str msg = 'Would you like to (locally) update digest ref files and/or versions in References.py?'
bool update_local_files = False
 not_in_athena_dir = subprocess.call("git rev-parse --is-inside-work-tree", shell=True)
 commands = update_reference_files(not args.test_run, update_local_files)

Detailed Description

Updates reference files for a given MR, as well as related files (digest ref files, References.py)

This script should be run in the root directory of the athena repository, 
and you should pass in the URL of "CI Builds Summary" page for the MR you are interested in.
i.e. the link that you get from the MR under "Full details available on <this CI monitor view>"

So, for example, if you are interested in MR 66303, you would run this script as follows:
Tools/PROCTools/scripts/update_ci_reference_files.py https://bigpanda.cern.ch/ciview/?rel=MR-63410-2023-10-09-12-27

Running with --test-run will modify local files (so you can test that the changes make sense), and will also print out the commands which would have been executed. Nothing remote is changed! 
This is a good way to check that the proposed changes look rational before actually making in earnest.

Function Documentation

◆ create_dir_and_copy_refs()

create_dir_and_copy_refs ( test,
actually_update = False )
If called with actually_update=False, this function will return a list of commands which would have been executed.

Definition at line 299 of file update_ci_reference_files.py.

299def create_dir_and_copy_refs(test, actually_update=False):
300 """
301 If called with actually_update=False, this function will return a list of commands which would have been executed.
302 """
303 commands = []
304
305 # Nothing to do if test uses a shared reference
306 if test.shared_ref is True:
307 return commands
308
309 if test.new_version_directory not in dirs_created:
310 commands.append("mkdir -p " + test.new_version_directory)
311 dirs_created.append(test.new_version_directory)
312
313 # Copy new directory first, then copy old (in case the new MR did not touch all files)
314 # Important! Use no-clobber for second copy or we will overwrite the new data with old!
315 commands.append("cp " + test.copied_file_path + "* "+ test.new_version_directory+"/")
316 commands.append("cp -n " + test.existing_ref + "/* "+ test.new_version_directory+"/")
317 if actually_update:
318 print(' -> Copying files from {} to {}'.format(test.copied_file_path, test.new_version_directory))
319 try:
320 for command in commands:
321 try:
322 subprocess.call( command, shell=True)
323 except Exception as e:
324 print('Command failed due to:', e)
325 print('Do you have EOS available on this machine?')
326 except Exception as e:
327 print('FATAL: Unable to copy files due to:', e)
328 sys.exit(1)
329
330 f = open(test.new_version_directory+'/info.txt', 'w')
331 f.write('Merge URL: https://gitlab.cern.ch/atlas/athena/-/merge_requests/{}\n'.format(test.mr))
332 f.write('Date: {}\n'.format(test.date))
333 f.write('AMI: {}\n'.format(test.tag))
334 f.write('Test name: {}\n'.format(test.name))
335 f.write('Files copied from: {}\n'.format(test.copied_file_path))
336 f.close()
337
338 return commands
339
void print(char *figname, TCanvas *c1)

◆ extract_links_from_json()

extract_links_from_json ( url)

Definition at line 371 of file update_ci_reference_files.py.

371def extract_links_from_json(url):
372 headers = {'Accept': 'application/json'}
373 r = requests.get(url+'&json', headers=headers)
374 data = r.json()["rows_s"]
375 # First row is header.
376 # Currently this is: 'Release', 'Platform', 'Project', 'git branch<BR>(link to MR)', 'Job time stamp', 'git clone', 'Externals build', 'CMake config', 'Build time', 'Comp. Errors (w/warnings)', 'Test time', 'CI tests errors (w/warnings)', 'Host'
377 for project in data[1:]:
378 process_CI_Builds_Summary(project)
379
380

◆ handle_shared_refs()

handle_shared_refs ( )

Definition at line 381 of file update_ci_reference_files.py.

381def handle_shared_refs():
382 # Tests that are allowed to use the same reference. The key is the test that uses the
383 # reference of its value.
384 shared_refs = {
385 'CITest_DerivationRun2Data_PHYS_MT-test': 'CITest_DerivationRun2Data_PHYS-test',
386 'CITest_DerivationRun2MC_PHYS_MT-test': 'CITest_DerivationRun2MC_PHYS-test',
387 'CITest_DerivationRun3Data_PHYS_MT-test': 'CITest_DerivationRun3Data_PHYS-test',
388 'CITest_DerivationRun3MC_PHYS_MT-test': 'CITest_DerivationRun3MC_PHYS-test',
389 'CITest_DerivationRun2Data_PHYSLITE_MT-test': 'CITest_DerivationRun2Data_PHYSLITE-test',
390 'CITest_DerivationRun2MC_PHYSLITE_MT-test': 'CITest_DerivationRun2MC_PHYSLITE-test',
391 'CITest_DerivationRun3Data_PHYSLITE_MT-test': 'CITest_DerivationRun3Data_PHYSLITE-test',
392 'CITest_DerivationRun3MC_PHYSLITE_MT-test': 'CITest_DerivationRun3MC_PHYSLITE-test',
393 }
394
395 for branch,tests in failing_tests.items():
396 # Create dictionary of ref vs tests
397 refs = defaultdict(list) # tag : [test,...]
398 for test in tests:
399 refs[test.tag].append(test)
400
401 for r, dups in refs.items():
402 if len(dups) <= 1:
403 continue
404 for test in dups:
405 # Mark test as having shared ref if itself and its reference is in the list
406 if (name := shared_refs.get(test.name)) and any(name==t.name for t in dups):
407 test.shared_ref = True
408
409

◆ process_CI_Builds_Summary()

process_CI_Builds_Summary ( project)

Definition at line 356 of file update_ci_reference_files.py.

356def process_CI_Builds_Summary(project):
357 # Each entry is one column in the table. 11th is the tests column.
358 # URL to tests page is in form:
359 # <a href="/testsview/?nightly=MR-CI-builds&rel=MR-66303-2023-10-10-19-08&ar=x86_64-centos7-gcc112-opt&proj=AthGeneration">0 (0)</a>
360 test_counts = strip_href(project[11])
361 # This is e.g. '0 (0)'
362 test_error_counts = int(test_counts.split(' ')[0])
363 if test_error_counts > 0:
364 # Okay, we have an error!
365 project_url = 'https://bigpanda.cern.ch'+strip_url(project[11])
366 headers = {'Accept': 'application/json'}
367 r = requests.get(project_url+'&json', headers=headers)
368 data = r.json()["rows_s"]
369 process_CI_Tests_json(data[1:])
370

◆ process_CI_Tests_json()

process_CI_Tests_json ( data)

Definition at line 340 of file update_ci_reference_files.py.

340def process_CI_Tests_json(data):
341 # Each list entry is one column in the table.
342 for row in data:
343 if ('ERROR' in row[0]):
344 process_log_file(strip_url(row[2]), branch = row[1], test_name=strip_href(row[2]))
345

◆ process_diffpool_change()

process_diffpool_change ( text,
ami_tag,
mr_number,
human_readable_date,
test_name )

Definition at line 117 of file update_ci_reference_files.py.

117def process_diffpool_change(text, ami_tag, mr_number, human_readable_date, test_name):
118 eos_path_root = '/eos/atlas/atlascerngroupdisk/data-art/grid-input/WorkflowReferences/'
119
120 # Copied file path
121 # e.g. from ERROR Copied '../SimulationRun3FullSim/run_s4006/myHITS.pool.root' to '/eos/atlas/atlascerngroupdisk/proj-ascig/gitlabci/MR63410_a84345c776e93f0d7f25d00c9e91e35bcb965d09/SimulationRun3FullSimChecks'
122 copied_file_match = re.search(r'^ERROR Copied.*', text, flags=re.MULTILINE)
123 if not copied_file_match:
124 print("FATAL: Could not find matching copied file")
125 sys.exit(1)
126 copied_file_path = copied_file_match.group().split('to')[1].strip().strip("'").strip("&#x27;")+'/'
127
128 # Reference file paths
129 ref_file_match = re.search(r'INFO Reading the reference file from location.*', text)
130 if not ref_file_match:
131 print("FATAL: Could not find matching reference file")
132 sys.exit(1)
133
134 ref_file_path = ref_file_match.group().split('location')[1].strip()
135 existing_version_number= ref_file_path.split('/')[-2]
136 branch = ref_file_path.split('/')[-4]
137 new_version_number = 'v'+str(int(existing_version_number[1:])+1)
138 new_version_directory = eos_path_root+branch+'/'+ami_tag+'/'+new_version_number
139 old_version_directory = eos_path_root+branch+'/'+ami_tag+'/'+existing_version_number
140 # Copied file path
141 # e.g. from ERROR Copied '../SimulationRun3FullSim/run_s4006/myHITS.pool.root' to '/eos/atlas/atlascerngroupdisk/proj-ascig/gitlabci/MR63410_a84345c776e93f0d7f25d00c9e91e35bcb965d09/SimulationRun3FullSimChecks'
142 copied_file_match = re.search(r'^ERROR Copied.*', text, flags=re.MULTILINE)
143 if not copied_file_match:
144 print("FATAL: Could not find matching copied file")
145 sys.exit(1)
146
147 # Sanity checks
148 ami_tag_check = ref_file_path.split('/')[-3].strip()
149 if ami_tag_check!=ami_tag:
150 print('FATAL: Sanity check: "{}" from reference file path "{}" does not match ami tag "{}" extracted previously.'.format(ami_tag_check, ref_file_path, ami_tag))
151 sys.exit(1)
152
153
154 test = CITest(name=test_name, tag=ami_tag, mr=mr_number, date=human_readable_date, existing_ref = old_version_directory, existing_version = existing_version_number, new_version = new_version_number, new_version_directory = new_version_directory, copied_file_path = copied_file_path, diff=None, type='DiffPool')
155 return test
156
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179

◆ process_digest_change()

process_digest_change ( text,
ami_tag,
mr_number,
human_readable_date,
test_name )

Definition at line 157 of file update_ci_reference_files.py.

157def process_digest_change(text, ami_tag, mr_number, human_readable_date, test_name):
158 # Some things aren't so relevant for digest changes
159 existing_version_number = None
160 new_version_directory = None
161 copied_file_path = None
162 new_version_number=None
163
164 # differs from the reference 'q447_AOD_digest.ref' (<):
165 ref_file_match = re.search(
166 r"differs from the reference (?:'|&#x27;)([^'&]+?)(?:'|&#x27;)",
167 text
168 )
169 if not ref_file_match:
170 print("FATAL: Could not find matching reference file")
171 sys.exit(1)
172 ref_file_path = ref_file_match.group(1)
173
174 diff_lines = []
175 diff_started = False # Once we hit the beginning of the diff, we start recording
176 # Diff starts with e.g.
177 # ERROR The output 'q449_AOD_digest.txt' (>) differs from the reference 'q449_AOD_digest.ref' (<):
178 # and ends with next INFO line
179
180 for line in text.split('\n'):
181 if 'differs from the reference' in line:
182 # Start of the diff
183 diff_started = True
184 elif diff_started:
185 if 'INFO' in line:
186 # End of the diff
187 break
188 elif len(line)>0:
189 diff_lines.append(html.unescape(line))
190
191 test = CITest(name=test_name, tag=ami_tag, mr=mr_number, date=human_readable_date, existing_ref = ref_file_path, existing_version = existing_version_number, new_version = new_version_number, new_version_directory = new_version_directory, copied_file_path = copied_file_path, diff=diff_lines, type='Content' if 'content.ref' in ref_file_path else 'Digest')
192 return test
193

◆ process_log_file()

process_log_file ( url,
branch,
test_name )
So now we have a URL to a failing test.
We need to check that the test is failing for the correct reason - namely a reference file which needs updating
The information we need to collect is:
- the AMI tag of the failing tests
- the merge request number
- the location of the reference file
- the location of the copied file
- the name of the test
- the new version number
- the new version directory

Definition at line 64 of file update_ci_reference_files.py.

64def process_log_file(url, branch, test_name):
65 """So now we have a URL to a failing test.
66 We need to check that the test is failing for the correct reason - namely a reference file which needs updating
67 The information we need to collect is:
68 - the AMI tag of the failing tests
69 - the merge request number
70 - the location of the reference file
71 - the location of the copied file
72 - the name of the test
73 - the new version number
74 - the new version directory
75 """
76 page = requests.get(url)
77 text = page.text
78
79 # First check that this looks like a test whose ref files need updating, bail otherwise
80 # INFO All q442 athena steps completed successfully
81 test_match = re.search(r'All (?P<ami_tag>\w+) athena steps completed successfully', text)
82 ami_tag = test_match.group('ami_tag') if test_match else None
83
84 # We have two types of tests, but lets try to extract some common information
85 if not ami_tag:
86 # Okay, maybe it was truncated? Try again.
87 match_attempt_2 = re.search(r'AMIConfig (?P<ami_tag>\w+)', text)
88 if match_attempt_2:
89 ami_tag = match_attempt_2.group('ami_tag')
90
91 if not ami_tag:
92 print('WARNING: Did not find an AMI tag in the test "{}". Ignoring.'.format(test_name))
93 return
94
95 mr_match = re.search(r'ARDOC_TestLog_MR-(?P<mr_number>\d+)-(?P<date>\d{4}-\d{2}-\d{2}-\d{2}-\d{2})', url)
96 if not mr_match:
97 print('FATAL: Could not process the URL as expected. Aborting.')
98 print(url)
99 sys.exit(1)
100
101 mr_number = mr_match.group('mr_number')
102 date = mr_match.group('date')
103 human_readable_date = ':'.join(date.split('-')[0:3]) + " at " + ':'.join(date.split('-')[3:])
104
105 if "Your change breaks the digest in test" in text or 'ERROR Your change modifies the output in test' in text:
106 # Okay, we have a digest change
107 failing_tests[branch].append(process_digest_change(text, ami_tag, mr_number, human_readable_date, test_name))
108
109 if ('ERROR Your change affects standard outputs in test' in text or
110 'ERROR Your change breaks the frozen tier0 policy in test' in text or
111 'ERROR Your change breaks the frozen derivation policy in test' in text):
112 # DiffPool change
113 failing_tests[branch].append(process_diffpool_change(text, ami_tag, mr_number, human_readable_date, test_name))
114
115 return
116

◆ strip_href()

strip_href ( href)

Definition at line 351 of file update_ci_reference_files.py.

351def strip_href(href):
352 value = href[href.find('>')+1:] # Strip everything up to first >
353 value = value[:value.find('<')]
354 return value
355

◆ strip_url()

strip_url ( href)

Definition at line 346 of file update_ci_reference_files.py.

346def strip_url(href):
347 url = href[href.find('"')+1:] # Strip everything up to first quotation mark
348 url = url[:url.find('"')]
349 return url
350

◆ summarise_failing_tests()

summarise_failing_tests ( check_for_duplicates = True)

Definition at line 410 of file update_ci_reference_files.py.

410def summarise_failing_tests(check_for_duplicates = True):
411 print('Summary of tests which need work:')
412
413 if not failing_tests:
414 print(" -> None found. Aborting.")
415 return None
416
417 mr = None
418 reference_folders = []
419 for branch,tests in failing_tests.items():
420 print (' * Branch: {}'.format(branch))
421 for test in tests:
422 print(' - ', test)
423 if test.type == 'DiffPool':
424 if not test.new_version_directory:
425 print('FATAL: No path to "new version" for test {} of type DiffPool.'.format(test.name))
426 sys.exit(1)
427
428 if os.path.exists(test.new_version_directory):
429 msg = f'WARNING: The directory {test.new_version_directory} already exists. Are you sure you want to overwrite the existing references?'
430 if input("%s (y/N) " % msg).lower() != 'y':
431 sys.exit(1)
432
433 if (not test.shared_ref and test.existing_ref not in reference_folders):
434 reference_folders.append(test.existing_ref)
435 elif check_for_duplicates and not test.shared_ref:
436 print('FATAL: Found two tests which both change the same reference file: {}, which is not supported.'.format(test.existing_ref))
437 print('Consider running again in --test-run mode, to get a copy of the copy commands that could be run.')
438 print('The general advice is to take the largest file (since it will have the most events).')
439 sys.exit(1)
440 mr = test.mr
441 return 'https://gitlab.cern.ch/atlas/athena/-/merge_requests/'+mr
442

◆ update_reference_files()

update_reference_files ( actually_update = True,
update_local_files = False )

Definition at line 194 of file update_ci_reference_files.py.

194def update_reference_files(actually_update=True, update_local_files=False):
195 print()
196 print('Updating reference files')
197 print('========================')
198 commands = []
199 for branch, tests in failing_tests.items():
200 for test in tests:
201 print('Processing test: {} on branch {}'.format(test.name, branch))
202 if test.type == 'DiffPool':
203 if test.shared_ref:
204 print(' * This is a DiffPool test but uses a shared reference. No update needed.')
205 continue
206
207 print(' * This is a DiffPool test, and currently has version {} of {}. Will update References.py with new version.'.format(test.existing_version, test.tag))
208 if actually_update:
209 print(' -> The new version is: {}. Creating directory and copying files on EOS now.'.format(test.new_version))
210 create_dir_and_copy_refs(test, True)
211 else:
212 # We will print these later, so we can sanity check them when in test mode
213 commands.extend(create_dir_and_copy_refs(test, False))
214 # Remove any duplicates, whilst preserving the order
215 commands = list(dict.fromkeys(commands))
216
217 # Now, update local References.py file
218 if update_local_files:
219 data = []
220 if debug:
221 print ('Updating local References.py file with new version {} for tag {}'.format(test.new_version, test.tag))
222 line_found = False
223 with open('Tools/WorkflowTestRunner/python/References.py', 'r') as f:
224 lines = f.readlines()
225 for line in lines:
226 if test.tag in line:
227 if test.existing_version in line:
228 line = line.replace(test.existing_version, test.new_version)
229 else:
230 print('')
231 print('** WARNING: For tag {} we were looking for existing version {}, but the line in the file is: {}'.format(test.tag, test.existing_version, line), end='')
232 print('** Are you sure your branch is up-to-date with main? We cannot update an older version of References.py!')
233 line_found = True
234 data.append(line)
235
236 if not line_found:
237 print('** WARNING - no matching line was found for the AMI tag {} in References.py. Are you sure your branch is up-to-date with main? We cannot update an older version of References.py!'.format(test.tag))
238
239 with open('Tools/WorkflowTestRunner/python/References.py', 'w') as f:
240 f.writelines(data)
241 elif test.type == 'Digest' and update_local_files:
242 print(' * This is a Digest test. Need to update reference file {}.'.format(test.existing_ref))
243 data = []
244
245 diff_line=0 # We will use this to keep track of which line in the diff we are on
246 digest_old = [line for line in test.diff if line.startswith('<')]
247 digest_new = [line for line in test.diff if line.startswith('>')]
248
249 with open('Tools/PROCTools/data/'+test.existing_ref, 'r') as f:
250 lines = f.readlines()
251 for current_line, line in enumerate(lines):
252 split_curr_line = line.split()
253 if (split_curr_line[0] == 'run'): # Skip header line
254 data.append(line)
255 continue
256
257 # So, we expect first two numbers to be run/event respectively
258 if (not split_curr_line[0].isnumeric()) or (not split_curr_line[1].isnumeric()):
259 print('FATAL: Found a line in current digest which does not start with run/event numbers: {}'.format(line))
260 sys.exit(1)
261
262 split_old_diff_line = digest_old[diff_line].split()
263 split_old_diff_line.pop(0) # Remove the < character
264 split_new_diff_line = digest_new[diff_line].split()
265 split_new_diff_line.pop(0) # Remove the > character
266
267 # Let's check to see if the run/event numbers match
268 if split_curr_line[0] == split_old_diff_line[0] and split_curr_line[1] == split_old_diff_line[1]:
269 # Okay so run/event numbers match. Let's just double-check it wasn't already updated
270 if split_curr_line!=split_old_diff_line:
271 print('FATAL: It seems like this line was already changed.')
272 print('Line we expected: {}'.format(test.old_diff_lines[diff_line]))
273 print('Line we got : {}'.format(line))
274 sys.exit(1)
275
276 # Check if the new run/event numbers match
277 if split_curr_line[0] == split_new_diff_line[0] and split_curr_line[1] == split_new_diff_line[1]:
278 #Replace the existing line with the new one, making sure we right align within 12 characters
279 data.append("".join(["{:>12}".format(x) for x in split_new_diff_line])+ '\n')
280 if ((diff_line+1)<len(digest_old)):
281 diff_line+=1
282 continue
283
284 # Otherwise, we just keep the existing line
285 data.append(line)
286
287 print(' -> Updating PROCTools digest file {}'.format(test.existing_ref))
288 with open('Tools/PROCTools/data/'+test.existing_ref, 'w') as f:
289 f.writelines(data)
290 elif test.type == 'Content' and update_local_files:
291 print(' * This is a Content test. Need to update reference file {}.'.format(test.existing_ref))
292 subprocess.run(f'patch --quiet Tools/PROCTools/data/{test.existing_ref}',
293 input='\n'.join(test.diff)+'\n',
294 text=True, shell=True, check=True)
295
296 return commands
297
298

Variable Documentation

◆ action

python.update_ci_reference_files.action

Definition at line 447 of file update_ci_reference_files.py.

◆ args

python.update_ci_reference_files.args = parser.parse_args()

Definition at line 448 of file update_ci_reference_files.py.

◆ author

python.update_ci_reference_files.author = mr.author['username']

Definition at line 471 of file update_ci_reference_files.py.

◆ commands

python.update_ci_reference_files.commands = update_reference_files(not args.test_run, update_local_files)

Definition at line 491 of file update_ci_reference_files.py.

◆ debug

bool python.update_ci_reference_files.debug = False

Definition at line 62 of file update_ci_reference_files.py.

◆ dirs_created

list python.update_ci_reference_files.dirs_created = []

Definition at line 61 of file update_ci_reference_files.py.

◆ failing_tests

python.update_ci_reference_files.failing_tests = defaultdict(list)

Definition at line 60 of file update_ci_reference_files.py.

◆ gl_project

python.update_ci_reference_files.gl_project = gitlab.Gitlab("https://gitlab.cern.ch").projects.get("atlas/athena")

Definition at line 469 of file update_ci_reference_files.py.

◆ help

python.update_ci_reference_files.help

Definition at line 446 of file update_ci_reference_files.py.

◆ local_branch

str python.update_ci_reference_files.local_branch = f'mr-{mr.iid}'

Definition at line 473 of file update_ci_reference_files.py.

◆ mr

python.update_ci_reference_files.mr = gl_project.mergerequests.get(mr_url.split('/')[-1])

Definition at line 470 of file update_ci_reference_files.py.

◆ mr_url

python.update_ci_reference_files.mr_url = summarise_failing_tests(not args.test_run)

Definition at line 463 of file update_ci_reference_files.py.

◆ msg

str python.update_ci_reference_files.msg = 'Would you like to (locally) update digest ref files and/or versions in References.py?'

Definition at line 482 of file update_ci_reference_files.py.

◆ not_in_athena_dir

python.update_ci_reference_files.not_in_athena_dir = subprocess.call("git rev-parse --is-inside-work-tree", shell=True)

Definition at line 485 of file update_ci_reference_files.py.

◆ parser

python.update_ci_reference_files.parser
Initial value:
= argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)

Definition at line 444 of file update_ci_reference_files.py.

◆ remote

str python.update_ci_reference_files.remote = f'https://:@gitlab.cern.ch:8443/{author}/athena.git'

Definition at line 472 of file update_ci_reference_files.py.

◆ update_local_files

bool update_local_files = False

Definition at line 483 of file update_ci_reference_files.py.