Skip to content

Commit 7319def

Browse files
Add job name pattern filtering to workflow error script
This commit introduces a new feature to filter jobs by name using a regular expression in the `print_workflow_run_errors.py` script. New features: - Added `--job-pattern` argument, which accepts a regex string. Only jobs whose names match this pattern will be processed for failures. - The default value for `--job-pattern` is `'^build.*'`, meaning by default, the script will only look at jobs starting with 'build'. Behavior: - Job name filtering is applied before checking for job failures. - If an invalid regex is provided for `--job-pattern`, the script prints an error and exits gracefully. - This new filter works in conjunction with existing log processing options (like `--grep-pattern` and `--all-failed-steps`), which are applied to the jobs that pass the name pattern filter and are failed.
1 parent 2f0aab6 commit 7319def

File tree

1 file changed

+44
-15
lines changed

1 file changed

+44
-15
lines changed

scripts/print_workflow_run_errors.py

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ def parse_repo_url_arg(url_string):
111111
parser.add_argument(
112112
"--workflow", "--workflow-name",
113113
type=str,
114-
default="integration_test.yml",
115-
help="Name of the workflow file (e.g., 'main.yml' or 'build-test.yml'). Default: 'integration_test.yml'."
114+
default="integration_tests.yml",
115+
help="Name of the workflow file (e.g., 'main.yml' or 'build-test.yml'). Default: 'integration_tests.yml'."
116116
)
117117
parser.add_argument(
118118
"--branch",
@@ -168,6 +168,12 @@ def parse_repo_url_arg(url_string):
168168
default=10,
169169
help="Number of lines of leading and trailing context to print for grep matches. Default: 10."
170170
)
171+
parser.add_argument(
172+
"--job-pattern",
173+
type=str,
174+
default='^build.*',
175+
help="Regular expression to filter job names. Only jobs matching this pattern will be processed. Default: '^build.*'"
176+
)
171177

172178
args = parser.parse_args()
173179
error_suffix = " (use --help for more details)"
@@ -258,24 +264,47 @@ def parse_repo_url_arg(url_string):
258264

259265
sys.stderr.write(f"Found workflow run ID: {run['id']} (Status: {run.get('status')}, Conclusion: {run.get('conclusion')})\n")
260266

261-
failed_jobs = get_failed_jobs_for_run(args.token, run['id'])
267+
try:
268+
job_name_pattern = re.compile(args.job_pattern)
269+
except re.error as e:
270+
sys.stderr.write(f"Error: Invalid regex for --job-pattern '{args.job_pattern}': {e}\n")
271+
sys.exit(1)
272+
273+
# 1. Fetch all jobs for the run
274+
all_jobs_api_response = get_all_jobs_for_run(args.token, run['id'])
275+
276+
if all_jobs_api_response is None: # Indicates an API error during fetch
277+
sys.stderr.write(f"Could not retrieve jobs for workflow run ID: {run['id']}. Exiting.\n")
278+
sys.exit(1)
279+
280+
# 2. Filter jobs by name pattern
281+
name_matching_jobs = [job for job in all_jobs_api_response if job_name_pattern.search(job['name'])]
282+
283+
if not name_matching_jobs:
284+
sys.stderr.write(f"No jobs found matching pattern '{args.job_pattern}' in workflow run ID: {run['id']}.\n")
285+
sys.exit(0)
286+
287+
sys.stderr.write(f"Found {len(name_matching_jobs)} job(s) matching pattern '{args.job_pattern}'. Checking for failures...\n")
288+
289+
# 3. From the name-matching jobs, find the ones that actually failed
290+
failed_jobs_matching_criteria = [job for job in name_matching_jobs if job.get('conclusion') == 'failure']
262291

263-
if not failed_jobs:
264-
sys.stderr.write(f"No failed jobs found for workflow run ID: {run['id']}.\n")
292+
if not failed_jobs_matching_criteria:
293+
sys.stderr.write(f"No failed jobs found among those matching pattern '{args.job_pattern}' for workflow run ID: {run['id']}.\n")
265294
if run.get('conclusion') == 'success':
266-
print(f"Workflow run {run['id']} completed successfully with no failed jobs.")
295+
print(f"Workflow run {run['id']} ({run.get('html_url', 'N/A')}) completed successfully. No jobs matching pattern '{args.job_pattern}' failed.")
267296
elif run.get('status') == 'in_progress' and run.get('conclusion') is None:
268-
print(f"Workflow run {run['id']} is still in progress. No failed jobs reported yet.")
297+
print(f"Workflow run {run['id']} ({run.get('html_url', 'N/A')}) is still in progress. No jobs matching pattern '{args.job_pattern}' have failed yet.")
269298
else:
270-
# This case might indicate the workflow failed but not at a job level,
271-
# or jobs are still pending/running.
272-
print(f"Workflow run {run['id']} has conclusion '{run.get('conclusion')}' but no specific failed jobs were identified by this script's criteria.")
299+
print(f"Workflow run {run['id']} ({run.get('html_url', 'N/A')}) has conclusion '{run.get('conclusion')}', but no jobs matching pattern ('{args.job_pattern}') were found to have failed.")
273300
sys.exit(0)
274301

275-
print(f"\n--- Failed Jobs for Workflow Run ID: {run['id']} ({run.get('html_url', 'No URL')}) ---\n")
302+
print(f"\n--- Failed Jobs (matching pattern '{args.job_pattern}') for Workflow Run ID: {run['id']} ({run.get('html_url', 'No URL')}) ---\n")
276303

277-
for job in failed_jobs:
304+
for job in failed_jobs_matching_criteria:
278305
print(f"==================================================================================")
306+
# Keep the job pattern in the individual job heading for clarity if needed, or remove if too verbose.
307+
# For now, let's assume it's clear from the main heading.
279308
print(f"Job: {job['name']} (ID: {job['id']}) - FAILED")
280309
print(f"Job URL: {job.get('html_url', 'N/A')}")
281310
print(f"==================================================================================")
@@ -405,8 +434,8 @@ def get_latest_workflow_run(token, workflow_name, branch_name):
405434
return None
406435

407436

408-
def get_failed_jobs_for_run(token, run_id):
409-
"""Fetches all jobs for a given workflow run and filters for failed ones."""
437+
def get_all_jobs_for_run(token, run_id):
438+
"""Fetches all jobs for a given workflow run."""
410439
url = f'{GITHUB_API_URL}/actions/runs/{run_id}/jobs'
411440
headers = {'Accept': 'application/vnd.github.v3+json', 'Authorization': f'token {token}'}
412441

@@ -437,7 +466,7 @@ def get_failed_jobs_for_run(token, run_id):
437466
return None
438467

439468
failed_jobs = [job for job in all_jobs if job.get('conclusion') == 'failure']
440-
return failed_jobs
469+
return all_jobs # Return all jobs, filtering happens in main
441470

442471

443472
def get_job_logs(token, job_id):

0 commit comments

Comments
 (0)