Spaces:
				
			
			
	
			
			
		Paused
		
	
	
	
			
			
	
	
	
	
		
		
		Paused
		
	| # Copyright 2020-2025 The HuggingFace Team. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import argparse | |
| import json | |
| import logging | |
| import os | |
| from datetime import date | |
| from pathlib import Path | |
| from tabulate import tabulate | |
| MAX_LEN_MESSAGE = 2900 # Slack endpoint has a limit of 3001 characters | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--slack_channel_name", default="trl-push-ci") | |
| # Set up logging | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") | |
| def process_log_file(log): | |
| failed_tests = [] | |
| passed_tests = [] | |
| section_num_failed = 0 | |
| try: | |
| with open(log) as f: | |
| for line in f: | |
| try: | |
| data = json.loads(line) | |
| test_name = data.get("nodeid", "") | |
| duration = f"{data['duration']:.4f}" if "duration" in data else "N/A" | |
| outcome = data.get("outcome", "") | |
| if test_name: | |
| if outcome == "failed": | |
| section_num_failed += 1 | |
| failed_tests.append([test_name, duration, log.stem.split("_")[0]]) | |
| else: | |
| passed_tests.append([test_name, duration, log.stem.split("_")[0]]) | |
| except json.JSONDecodeError as e: | |
| logging.warning(f"Could not decode line in {log}: {e}") | |
| except FileNotFoundError as e: | |
| logging.error(f"Log file {log} not found: {e}") | |
| except Exception as e: | |
| logging.error(f"Error processing log file {log}: {e}") | |
| return failed_tests, passed_tests, section_num_failed | |
| def main(slack_channel_name): | |
| group_info = [] | |
| total_num_failed = 0 | |
| total_empty_files = [] | |
| log_files = list(Path().glob("*.log")) | |
| if not log_files: | |
| logging.info("No log files found.") | |
| return | |
| for log in log_files: | |
| failed, passed, section_num_failed = process_log_file(log) | |
| empty_file = not failed and not passed | |
| total_num_failed += section_num_failed | |
| total_empty_files.append(empty_file) | |
| group_info.append([str(log), section_num_failed, failed]) | |
| # Clean up log file | |
| try: | |
| os.remove(log) | |
| except OSError as e: | |
| logging.warning(f"Could not remove log file {log}: {e}") | |
| # Prepare Slack message payload | |
| payload = [ | |
| { | |
| "type": "header", | |
| "text": {"type": "plain_text", "text": f"π€ Results of the {os.environ.get('TEST_TYPE', '')} TRL tests."}, | |
| }, | |
| ] | |
| if total_num_failed > 0: | |
| message = "" | |
| for name, num_failed, failed_tests in group_info: | |
| if num_failed > 0: | |
| message += f"*{name}: {num_failed} failed test(s)*\n" | |
| failed_table = [ | |
| test[0].split("::")[:2] + [test[0].split("::")[-1][:30] + ".."] for test in failed_tests | |
| ] | |
| message += ( | |
| "\n```\n" | |
| + tabulate(failed_table, headers=["Test Location", "Test Name"], tablefmt="grid") | |
| + "\n```\n" | |
| ) | |
| if any(total_empty_files): | |
| message += f"\n*{name}: Warning! Empty file - check GitHub action job*\n" | |
| # Logging | |
| logging.info(f"Total failed tests: {total_num_failed}") | |
| print(f"### {message}") | |
| if len(message) > MAX_LEN_MESSAGE: | |
| message = ( | |
| f"β There are {total_num_failed} failed tests in total! Please check the action results directly." | |
| ) | |
| payload.append({"type": "section", "text": {"type": "mrkdwn", "text": message}}) | |
| payload.append( | |
| { | |
| "type": "section", | |
| "text": {"type": "mrkdwn", "text": "*For more details:*"}, | |
| "accessory": { | |
| "type": "button", | |
| "text": {"type": "plain_text", "text": "Check Action results"}, | |
| "url": f"https://github.com/huggingface/trl/actions/runs/{os.environ['GITHUB_RUN_ID']}", | |
| }, | |
| } | |
| ) | |
| payload.append( | |
| { | |
| "type": "context", | |
| "elements": [ | |
| { | |
| "type": "plain_text", | |
| "text": f"On Push main {os.environ.get('TEST_TYPE')} results for {date.today()}", | |
| } | |
| ], | |
| } | |
| ) | |
| # Send to Slack | |
| from slack_sdk import WebClient | |
| slack_client = WebClient(token=os.environ.get("SLACK_API_TOKEN")) | |
| slack_client.chat_postMessage(channel=f"#{slack_channel_name}", text=message, blocks=payload) | |
| else: | |
| payload.append( | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "β No failures! All tests passed successfully.", | |
| "emoji": True, | |
| }, | |
| } | |
| ) | |
| logging.info("All tests passed. No errors detected.") | |
| if __name__ == "__main__": | |
| args = parser.parse_args() | |
| main(args.slack_channel_name) | |