Skip to content

Commit bcdf292

Browse files
Enhance workflow error script with options and defaults
This commit updates the `print_workflow_run_errors.py` script: - Workflow name and branch are now optional arguments: - `--workflow` (or `--workflow-name`) defaults to "integration_test.yml". - `--branch` defaults to the current Git branch. - Changed default log lines printed from 500 to 100 (`--log-lines`). - Added `--all-failed-steps` flag: - If false (default), only logs for the first failed step in a job are printed. - If true, logs for all failed steps in a job are printed. These changes provide more flexibility and sensible defaults for common use cases.
1 parent 33bd1f3 commit bcdf292

File tree

1 file changed

+46
-35
lines changed

1 file changed

+46
-35
lines changed

scripts/print_workflow_run_errors.py

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ def requests_retry_session(retries=RETRIES,
6565
return session
6666

6767

68+
def get_current_branch_name():
69+
"""Gets the current git branch name."""
70+
try:
71+
branch_bytes = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], stderr=subprocess.PIPE)
72+
return branch_bytes.decode().strip()
73+
except (subprocess.CalledProcessError, FileNotFoundError, UnicodeDecodeError) as e:
74+
sys.stderr.write(f"Info: Could not determine current git branch via 'git rev-parse --abbrev-ref HEAD': {e}. Branch will need to be specified.\n")
75+
return None
76+
except Exception as e: # Catch any other unexpected error.
77+
sys.stderr.write(f"Info: An unexpected error occurred while determining current git branch: {e}. Branch will need to be specified.\n")
78+
return None
79+
80+
6881
def main():
6982
"""Main function to parse arguments and orchestrate the script."""
7083
determined_owner = None
@@ -89,19 +102,23 @@ def parse_repo_url_arg(url_string):
89102
return url_match.group(1), url_match.group(2)
90103
return None, None
91104

105+
current_branch = get_current_branch_name()
106+
92107
parser = argparse.ArgumentParser(
93108
description="Fetch and display failed steps and their logs from a GitHub workflow run.",
94109
formatter_class=argparse.RawTextHelpFormatter
95110
)
96111
parser.add_argument(
97-
"workflow_name",
112+
"--workflow", "--workflow-name",
98113
type=str,
99-
help="Name of the workflow file (e.g., 'main.yml' or 'build-test.yml')."
114+
default="integration_test.yml",
115+
help="Name of the workflow file (e.g., 'main.yml' or 'build-test.yml'). Default: 'integration_test.yml'."
100116
)
101117
parser.add_argument(
102-
"branch",
118+
"--branch",
103119
type=str,
104-
help="GitHub branch name to check for the workflow run."
120+
default=current_branch,
121+
help=f"GitHub branch name to check for the workflow run. {'Default: ' + current_branch if current_branch else 'Required if not determinable from current git branch.'}"
105122
)
106123
parser.add_argument(
107124
"--url",
@@ -130,8 +147,14 @@ def parse_repo_url_arg(url_string):
130147
parser.add_argument(
131148
"--log-lines",
132149
type=int,
133-
default=500,
134-
help="Number of lines to print from the end of each failed step's log. Default: 500."
150+
default=100,
151+
help="Number of lines to print from the end of each failed step's log. Default: 100."
152+
)
153+
parser.add_argument(
154+
"--all-failed-steps",
155+
action="store_true",
156+
default=False,
157+
help="If set, print logs for all failed steps in a job. Default is to print logs only for the first failed step."
135158
)
136159

137160
args = parser.parse_args()
@@ -210,11 +233,15 @@ def parse_repo_url_arg(url_string):
210233
sys.stderr.write(f"Error: Could not set repository info to {final_owner}/{final_repo}. Ensure owner/repo are correct.{error_suffix}\n")
211234
sys.exit(1)
212235

213-
sys.stderr.write(f"Processing workflow '{args.workflow_name}' on branch '{args.branch}' for repo {OWNER}/{REPO}\n")
236+
if not args.branch:
237+
sys.stderr.write(f"Error: Branch name is required. Please specify --branch or ensure it can be detected from your current git repository.{error_suffix}\n")
238+
sys.exit(1)
239+
240+
sys.stderr.write(f"Processing workflow '{args.workflow}' on branch '{args.branch}' for repo {OWNER}/{REPO}\n")
214241

215-
run = get_latest_workflow_run(args.token, args.workflow_name, args.branch)
242+
run = get_latest_workflow_run(args.token, args.workflow, args.branch)
216243
if not run:
217-
sys.stderr.write(f"No workflow run found for workflow '{args.workflow_name}' on branch '{args.branch}'.\n")
244+
sys.stderr.write(f"No workflow run found for workflow '{args.workflow}' on branch '{args.branch}'.\n")
218245
sys.exit(0)
219246

220247
sys.stderr.write(f"Found workflow run ID: {run['id']} (Status: {run.get('status')}, Conclusion: {run.get('conclusion')})\n")
@@ -261,44 +288,27 @@ def parse_repo_url_arg(url_string):
261288
continue
262289

263290
print(f"\n--- Failed Steps in Job: {job['name']} ---")
291+
first_failed_step_logged = False
264292
for step in failed_steps_details:
293+
if not args.all_failed_steps and first_failed_step_logged:
294+
print(f"\n--- Skipping subsequent failed step: {step.get('name', 'Unnamed step')} (use --all-failed-steps to see all) ---")
295+
break # Stop after the first failed step if not --all-failed-steps
296+
265297
step_name = step.get('name', 'Unnamed step')
266298
print(f"\n--- Step: {step_name} ---")
267-
# Attempt to extract specific step log
268-
# GitHub log format: ##[group]Step Name ... ##[endgroup]
269-
# A simpler approach for now is to print the relevant section of the full job log
270-
# if we can identify it. If not, we might fall back to the full log or last N lines.
271-
# For now, we'll just print the last N lines of the *entire job log* for *each* failed step found by API,
272-
# as parsing the full log to attribute lines to specific steps is complex.
273-
# A more advanced implementation would parse the log structure.
274-
275-
# Simplistic approach: Print last N lines of the whole job log for context for this step.
276-
# This is not ideal as it doesn't isolate the step's specific log lines.
277-
# A better method would be to parse the job_logs string.
278-
279-
# Placeholder for more precise log extraction for the specific step
280-
# For now, we'll find the step in the log and print lines around it or from it.
281299

282300
# Crude log extraction:
283-
step_log_lines = []
284-
in_step_group = False
285301
# Regex to match group start, attempting to capture the step name robustly
286-
# Handles cases like "Run echo "hello"" where step['name'] is `Run echo "hello"`
287-
# and in logs it might be `##[group]Run echo "hello"`
288-
# We need to be careful with regex special characters in step_name
289302
escaped_step_name = re.escape(step_name)
290-
# Try to match common step prefixes if the exact name isn't found
291-
# This is still very heuristic.
292303
step_start_pattern = re.compile(r"^##\[group\](?:Run\s+|Setup\s+|Complete\s+)?.*?" + escaped_step_name, re.IGNORECASE)
293304
step_end_pattern = re.compile(r"^##\[endgroup\]")
294305

295306
current_step_log_segment = []
296307
capturing_for_failed_step = False
297-
298-
log_lines = job_logs.splitlines()
308+
log_lines_for_job = job_logs.splitlines() # Split once per job
299309

300310
# Try to find the specific step's log segment
301-
for line in log_lines:
311+
for line in log_lines_for_job:
302312
if step_start_pattern.search(line):
303313
capturing_for_failed_step = True
304314
current_step_log_segment = [line] # Start with the group line
@@ -308,7 +318,7 @@ def parse_repo_url_arg(url_string):
308318
if step_end_pattern.search(line):
309319
capturing_for_failed_step = False
310320
# Found the end of the targeted step's log
311-
break # Stop processing lines for this step
321+
break # Stop processing lines for this step (within this job's logs)
312322

313323
if current_step_log_segment:
314324
print(f"Log for failed step '{step_name}' (last {args.log_lines} lines of its segment):")
@@ -317,9 +327,10 @@ def parse_repo_url_arg(url_string):
317327
else:
318328
# Fallback if specific step log segment couldn't be reliably identified
319329
print(f"Could not isolate log for step '{step_name}'. Printing last {args.log_lines} lines of the entire job log as context:")
320-
for log_line in log_lines[-args.log_lines:]:
330+
for log_line in log_lines_for_job[-args.log_lines:]: # Use the job's split lines
321331
print(log_line)
322332
print(f"--- End of log for step: {step_name} ---")
333+
first_failed_step_logged = True # Mark that we've logged at least one step
323334

324335
print(f"\n--- End of Failed Steps for Job: {job['name']} ---\n")
325336

0 commit comments

Comments
 (0)