| """Validate the YAML files for GitHub Actions workflows. | |
| TODO: b/359303016 - convert to use unittest | |
| """ | |
| import os | |
| import re | |
| import yaml | |
| def check_ctest(namein, jobin, filein, obj): | |
| """Checks that the ctest command is not in a mode that allows missing tests to pass.""" | |
| for key in obj: | |
| if ( | |
| isinstance(obj[key], str) | |
| and 'ctest' in obj[key] | |
| and 'ctest --no-tests=error' not in obj[key] | |
| ): | |
| raise ValueError( | |
| 'Step %s in job %s from file %s runs ctest in a mode that allows' | |
| ' missing tests to pass. Add --no-tests=error to the command and' | |
| ' build with -Dprotobuf_BUILD_TESTS=ON.' % (namein, jobin, filein) | |
| ) | |
| elif isinstance(obj[key], dict): | |
| check_ctest(namein, jobin, filein, obj[key]) | |
| # Ensure every job is in the list of blocking jobs. | |
| with open( | |
| os.path.join(os.path.dirname(__file__), '../workflows/test_runner.yml'), 'r' | |
| ) as f: | |
| data = yaml.safe_load(f) | |
| # List of all YAML files that are used by jobs in the test_runner.yml file. | |
| yaml_files = [] | |
| # Get a list of all jobs in the test_runner, except for the blocking job and | |
| # the tag removal job, which is not always run. | |
| all_jobs = list(data['jobs'].keys()) | |
| all_jobs.remove('all-blocking-tests') | |
| all_jobs.remove('remove-tag') | |
| passed = True | |
| blocking_jobs = data['jobs']['all-blocking-tests']['needs'] | |
| for job in all_jobs: | |
| if 'uses' in data['jobs'][job]: | |
| yaml_files.append( | |
| os.path.join( | |
| os.path.dirname(__file__), | |
| '../workflows', | |
| os.path.basename(data['jobs'][job]['uses']), | |
| ) | |
| ) | |
| if job not in blocking_jobs: | |
| passed = False | |
| raise ValueError('Job %s is not in the list of blocking jobs.' % job) | |
| print('PASSED: All jobs are in the list of blocking jobs.') | |
| # Ensure every job with a continuous prefix conditions every step on whether we | |
| # are in a continuous run. | |
| for file in yaml_files: | |
| with open(file, 'r') as f: | |
| data = yaml.safe_load(f) | |
| jobs = data['jobs'] | |
| for job in jobs: | |
| if 'steps' not in jobs[job]: | |
| continue | |
| continuous_condition = 'inputs.continuous-prefix' in jobs[job]['name'] | |
| steps = jobs[job]['steps'] | |
| for step in steps: | |
| if 'name' in step: | |
| name = step['name'] | |
| elif 'with' in step and 'name' in step['with']: | |
| name = step['with']['name'] | |
| else: | |
| raise ValueError( | |
| 'Step in job %s from file %s does not have a name.' % (job, file) | |
| ) | |
| if continuous_condition and 'continuous-run' not in step.get('if', ''): | |
| raise ValueError( | |
| 'Step %s in job %s from file %s does not check the continuous-run' | |
| ' condition' % (name, job, file) | |
| ) | |
| if not continuous_condition and 'continuous-run' in step.get('if', ''): | |
| raise ValueError( | |
| 'Step %s in job %s from file %s checks the continuous-run' | |
| ' condition but the job does not contain the continuous-prefix' | |
| % (name, job, file) | |
| ) | |
| check_ctest(name, job, file, step) | |
| print('PASSED: All steps in all jobs check the continuous-run condition.') |