From b95140666bdb3376b35d39587db2411d4e3c4675 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 17 Mar 2025 00:20:13 -0400 Subject: [PATCH 1/3] ci: remove sunnylink pairing QR code from UI preview (#673) --- selfdrive/ui/tests/test_ui/run.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index f3fd6aa915..797a5f70b7 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -212,11 +212,6 @@ def setup_settings_sunnylink_sponsor_button(click, pm: PubMaster, scroll=None): click(1967, 225) time.sleep(UI_DELAY) -def setup_settings_sunnylink_pair_button(click, pm: PubMaster, scroll=None): - setup_settings_sunnylink(click, pm) - click(1967, 432) - time.sleep(UI_DELAY) - def setup_settings_sunnypilot(click, pm: PubMaster, scroll=None): setup_settings_device(click, pm) click(278, 852) @@ -279,7 +274,6 @@ CASES = { CASES.update({ "settings_sunnylink": setup_settings_sunnylink, "settings_sunnylink_sponsor_button": setup_settings_sunnylink_sponsor_button, - "settings_sunnylink_pair_button": setup_settings_sunnylink_pair_button, "settings_sunnypilot": setup_settings_sunnypilot, "settings_sunnypilot_mads": setup_settings_sunnypilot_mads, "settings_trips": setup_settings_trips, From 4a04819d964ec6360e2e1fc80c614fe4b8bdf01d Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 17 Mar 2025 00:36:32 -0400 Subject: [PATCH 2/3] Redirect license to LICENSE.md (#672) * Redirect license to LICENSE.md * bump --- opendbc_repo | 2 +- .../ui/sunnypilot/qt/network/networking.h | 2 +- sunnypilot/mads/helpers.py | 23 ++----------------- sunnypilot/mads/mads.py | 23 ++----------------- sunnypilot/mads/state.py | 23 ++----------------- .../mads/tests/test_mads_state_machine.py | 23 ++----------------- sunnypilot/modeld/runners/run_helpers.py | 10 ++++---- sunnypilot/models/fetcher.py | 10 ++++---- sunnypilot/models/helpers.py | 10 ++++---- sunnypilot/models/manager.py | 10 ++++---- .../models/tests/model_manager_audit.py | 10 ++++---- 11 files changed, 40 insertions(+), 106 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 9b83f9519d..9fbd19d3a6 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 9b83f9519db61c96facb5e20b35d69c13ef3bd91 +Subproject commit 9fbd19d3a6d45f5cd0bb979e0b1d2e2247246b85 diff --git a/selfdrive/ui/sunnypilot/qt/network/networking.h b/selfdrive/ui/sunnypilot/qt/network/networking.h index 3370751bf5..f220b8a50a 100644 --- a/selfdrive/ui/sunnypilot/qt/network/networking.h +++ b/selfdrive/ui/sunnypilot/qt/network/networking.h @@ -1,5 +1,5 @@ /** -* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. * * This file is part of sunnypilot and is licensed under the MIT License. * See the LICENSE.md file in the root directory for more details. diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py index 8a7ffe80ec..46f65c999b 100644 --- a/sunnypilot/mads/helpers.py +++ b/sunnypilot/mads/helpers.py @@ -1,27 +1,8 @@ """ -The MIT License - Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Last updated: July 29, 2024 +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. """ from openpilot.common.params import Params diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 9e53a8eeed..991e345ce8 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -1,27 +1,8 @@ """ -The MIT License - Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Last updated: July 29, 2024 +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. """ from cereal import car, log, custom diff --git a/sunnypilot/mads/state.py b/sunnypilot/mads/state.py index e3acf3a0e0..89ae506ac8 100644 --- a/sunnypilot/mads/state.py +++ b/sunnypilot/mads/state.py @@ -1,27 +1,8 @@ """ -The MIT License - Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Last updated: July 29, 2024 +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. """ from cereal import log, custom diff --git a/sunnypilot/mads/tests/test_mads_state_machine.py b/sunnypilot/mads/tests/test_mads_state_machine.py index 546893d285..51013b0baf 100644 --- a/sunnypilot/mads/tests/test_mads_state_machine.py +++ b/sunnypilot/mads/tests/test_mads_state_machine.py @@ -1,27 +1,8 @@ """ -The MIT License - Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Last updated: July 29, 2024 +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. """ import pytest diff --git a/sunnypilot/modeld/runners/run_helpers.py b/sunnypilot/modeld/runners/run_helpers.py index cc4c644583..5470106f1d 100644 --- a/sunnypilot/modeld/runners/run_helpers.py +++ b/sunnypilot/modeld/runners/run_helpers.py @@ -1,7 +1,9 @@ -# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -# -# This file is part of sunnypilot and is licensed under the MIT License. -# See the LICENSE.md file in the root directory for more details. +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" import os import pickle diff --git a/sunnypilot/models/fetcher.py b/sunnypilot/models/fetcher.py index f543b00a79..d9aa54aa88 100644 --- a/sunnypilot/models/fetcher.py +++ b/sunnypilot/models/fetcher.py @@ -1,7 +1,9 @@ -# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -# -# This file is part of sunnypilot and is licensed under the MIT License. -# See the LICENSE.md file in the root directory for more details. +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" import json import time diff --git a/sunnypilot/models/helpers.py b/sunnypilot/models/helpers.py index 915f1a4206..dbdef55900 100644 --- a/sunnypilot/models/helpers.py +++ b/sunnypilot/models/helpers.py @@ -1,7 +1,9 @@ -# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -# -# This file is part of sunnypilot and is licensed under the MIT License. -# See the LICENSE.md file in the root directory for more details. +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" import hashlib import os diff --git a/sunnypilot/models/manager.py b/sunnypilot/models/manager.py index 06705bd5ce..5fbad6cd8e 100644 --- a/sunnypilot/models/manager.py +++ b/sunnypilot/models/manager.py @@ -1,7 +1,9 @@ -# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -# -# This file is part of sunnypilot and is licensed under the MIT License. -# See the LICENSE.md file in the root directory for more details. +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" import asyncio import os diff --git a/sunnypilot/models/tests/model_manager_audit.py b/sunnypilot/models/tests/model_manager_audit.py index 1a1ea38e5b..4cd2b7d78e 100644 --- a/sunnypilot/models/tests/model_manager_audit.py +++ b/sunnypilot/models/tests/model_manager_audit.py @@ -1,7 +1,9 @@ -# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. -# -# This file is part of sunnypilot and is licensed under the MIT License. -# See the LICENSE.md file in the root directory for more details. +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" from cereal import messaging, custom From 3177142bfcb7555976b3de2f092ba0d7e93002d5 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 17 Mar 2025 03:49:27 -0400 Subject: [PATCH 3/3] ci: update existing comment during Squash and Merge (#674) * format * ci: update existing comment during Squash and Merge * dynamic target branch * test label * try again * no f * test * should be edit-last * quote * fix arg * final * bold it * revert --- release/ci/squash_and_merge_prs.py | 270 ++++++++++++++++------------- 1 file changed, 152 insertions(+), 118 deletions(-) diff --git a/release/ci/squash_and_merge_prs.py b/release/ci/squash_and_merge_prs.py index 28ffd5fc3a..3abe06705a 100755 --- a/release/ci/squash_and_merge_prs.py +++ b/release/ci/squash_and_merge_prs.py @@ -9,155 +9,189 @@ from datetime import datetime def setup_argument_parser(): - parser = argparse.ArgumentParser(description='Process and squash GitHub PRs') - parser.add_argument('--pr-data', type=str, help='PR data in JSON format') - parser.add_argument('--source-branch', type=str, default='master-new', - help='Source branch for merging') - parser.add_argument('--target-branch', type=str, default='master-dev-c3-new-test', - help='Target branch for merging') - parser.add_argument('--squash-script-path', type=str, required=True, - help='Path to the squash_and_merge.py script') - return parser + parser = argparse.ArgumentParser(description='Process and squash GitHub PRs') + parser.add_argument('--pr-data', type=str, help='PR data in JSON format') + parser.add_argument('--source-branch', type=str, default='master-new', + help='Source branch for merging') + parser.add_argument('--target-branch', type=str, default='master-dev-c3-new-test', + help='Target branch for merging') + parser.add_argument('--squash-script-path', type=str, required=True, + help='Path to the squash_and_merge.py script') + return parser def validate_squash_script(script_path): - if not os.path.isfile(script_path): - raise FileNotFoundError(f"Squash script not found at: {script_path}") - if not os.access(script_path, os.X_OK): - raise PermissionError(f"Squash script is not executable: {script_path}") + if not os.path.isfile(script_path): + raise FileNotFoundError(f"Squash script not found at: {script_path}") + if not os.access(script_path, os.X_OK): + raise PermissionError(f"Squash script is not executable: {script_path}") def sort_prs_by_creation(pr_data): - """Sort PRs by creation date""" - nodes = (pr_data.get('data', {}).get('search', {}).get('nodes', [])) + """Sort PRs by creation date""" + nodes = (pr_data.get('data', {}).get('search', {}).get('nodes', [])) - return sorted( - nodes, - key=lambda x: datetime.fromisoformat(x.get('createdAt', '').replace('Z', '+00:00')) - ) + return sorted( + nodes, + key=lambda x: datetime.fromisoformat(x.get('createdAt', '').replace('Z', '+00:00')) + ) def add_pr_comment(pr_number, comment): - """Add a comment to a PR using gh cli""" - try: - subprocess.run( - ['gh', 'pr', 'comment', str(pr_number), '--body', comment], - check=True, - capture_output=True, - text=True - ) - except subprocess.CalledProcessError as e: - print(f"Failed to add comment to PR #{pr_number}: {e.stderr}") + """Add or update a comment to a PR using gh cli""" + title = "## Squash and Merge" + + try: + result = subprocess.run( + ['gh', 'pr', 'view', str(pr_number), '--json', 'comments'], + check=True, + capture_output=True, + text=True + ) + + comments_data = json.loads(result.stdout) + has_existing_comment = False + + for pr_comment in comments_data['comments']: + if pr_comment['body'].startswith(title): + has_existing_comment = True + break + + full_comment = f"{title}\n\n{comment}" + + if has_existing_comment: + subprocess.run( + ['gh', 'pr', 'comment', '--edit-last', f"#{pr_number}", '--body', full_comment], + check=True, + capture_output=True, + text=True + ) + else: + subprocess.run( + ['gh', 'pr', 'comment', f"#{pr_number}", '--body', full_comment], + check=True, + capture_output=True, + text=True + ) + + except subprocess.CalledProcessError as e: + print(f"Failed to add/update comment on PR #{pr_number}: {e.stderr}") + except json.JSONDecodeError: + print(f"Failed to parse comments data for PR #{pr_number}") + def validate_pr(pr): - """Validate a PR and return (is_valid, skip_reason)""" - pr_number = pr.get('number', 'UNKNOWN') - branch = pr.get('headRefName', '') + """Validate a PR and return (is_valid, skip_reason)""" + pr_number = pr.get('number', 'UNKNOWN') + branch = pr.get('headRefName', '') - if not branch: - return False, f"Missing branch name for PR #{pr_number}" + if not branch: + return False, f"missing branch name for PR #{pr_number}" - # Check if checks have passed - commits = pr.get('commits', {}).get('nodes', []) - if not commits: - return False, "No commit data found" + # Check if checks have passed + commits = pr.get('commits', {}).get('nodes', []) + if not commits: + return False, "no commit data found" - status = commits[0].get('commit', {}).get('statusCheckRollup', {}) - if not status or status.get('state') != 'SUCCESS': - return False, "Not all checks have passed" + status = commits[0].get('commit', {}).get('statusCheckRollup', {}) + if not status or status.get('state') != 'SUCCESS': + return False, "not all checks have passed" - # Check for merge conflicts - merge_status = subprocess.run(['gh', 'pr', 'view', str(pr_number), '--json', 'mergeable,mergeStateStatus'], capture_output=True, text=True) - merge_data = json.loads(merge_status.stdout) - if not merge_data.get('mergeable'): - return False, "Merge conflicts detected" + # Check for merge conflicts + merge_status = subprocess.run(['gh', 'pr', 'view', str(pr_number), '--json', 'mergeable,mergeStateStatus'], + capture_output=True, text=True) + merge_data = json.loads(merge_status.stdout) + if not merge_data.get('mergeable'): + return False, "merge conflicts detected" - if (mergeStateStatus := merge_data.get('mergeStateStatus')) == "BEHIND": - return False, f"Branch is `{mergeStateStatus}`" + if (mergeStateStatus := merge_data.get('mergeStateStatus')) == "BEHIND": + return False, f"branch is `{mergeStateStatus}`" - return True, None + return True, None def process_pr(pr_data, source_branch, target_branch, squash_script_path): - try: - nodes = sort_prs_by_creation(pr_data) - if not nodes: - print("No PRs to squash") - return 0 + try: + nodes = sort_prs_by_creation(pr_data) + if not nodes: + print("No PRs to squash") + return 0 - print(f"Deleting target branch {target_branch}") - subprocess.run(['git', 'branch', '-D', target_branch], check=False) - subprocess.run(['git', 'branch', target_branch, f'origin/{source_branch}'], check=True) - success_count = 0 - for pr in nodes: - pr_number = pr.get('number', 'UNKNOWN') - branch = pr.get('headRefName', '') - title = pr.get('title', '') - is_valid, skip_reason = validate_pr(pr) + print(f"Deleting target branch {target_branch}") + subprocess.run(['git', 'branch', '-D', target_branch], check=False) + subprocess.run(['git', 'branch', target_branch, f'origin/{source_branch}'], check=True) + success_count = 0 + for pr in nodes: + pr_number = pr.get('number', 'UNKNOWN') + branch = pr.get('headRefName', '') + title = pr.get('title', '') + is_valid, skip_reason = validate_pr(pr) - if not is_valid: - print(f"Warning: {skip_reason} for PR #{pr_number}, skipping") - add_pr_comment(pr_number, f"⚠️ This PR was skipped in the automated `{target_branch}` squash because {skip_reason}.") - continue + if not is_valid: + print(f"Warning: {skip_reason} for PR #{pr_number}, skipping") + add_pr_comment(pr_number, + f"⚠️ This PR was skipped in the automated `{target_branch}` squash because **{skip_reason}**.") + continue - try: - # Fetch PR branch - subprocess.run(['git', 'fetch', 'origin', branch], check=True) - # Delete branch if it exists (ignore errors if it doesn't) - subprocess.run(['git', 'branch', '-D', branch], check=False) - # Create new branch pointing to origin's branch - subprocess.run(['git', 'branch', branch, f'origin/{branch}'], check=True) + try: + # Fetch PR branch + subprocess.run(['git', 'fetch', 'origin', branch], check=True) + # Delete branch if it exists (ignore errors if it doesn't) + subprocess.run(['git', 'branch', '-D', branch], check=False) + # Create new branch pointing to origin's branch + subprocess.run(['git', 'branch', branch, f'origin/{branch}'], check=True) - # Run squash script - subprocess.run([ - squash_script_path, - '--target', target_branch, - '--source', branch, - '--title', f"{title} (#{pr_number})", - ], check=True) + # Run squash script + subprocess.run([ + squash_script_path, + '--target', target_branch, + '--source', branch, + '--title', f"{title} (#{pr_number})", + ], check=True) - print(f"Successfully processed PR #{pr_number}") - success_count += 1 + print(f"Successfully processed PR #{pr_number}") + success_count += 1 - except subprocess.CalledProcessError as e: - print(f"Error processing PR #{pr_number}:") - print(f"Command failed with exit code {e.returncode}") - error_output = getattr(e, 'stderr', 'No error output available') - print(f"Error output: {error_output}") - add_pr_comment(pr_number, f"⚠️ Error during automated {target_branch} squash:\n```\n{error_output}\n```") - continue - except Exception as e: - print(f"Unexpected error processing PR #{pr_number}: {str(e)}") - continue + except subprocess.CalledProcessError as e: + print(f"Error processing PR #{pr_number}:") + print(f"Command failed with exit code {e.returncode}") + error_output = getattr(e, 'stderr', 'No error output available') + print(f"Error output: {error_output}") + add_pr_comment(pr_number, + f"⚠️ Error during automated `{target_branch}` squash:\n```\n{error_output}\n```") + continue + except Exception as e: + print(f"Unexpected error processing PR #{pr_number}: {str(e)}") + continue - return success_count - - except Exception as e: - import traceback - print(f"Error in process_pr: {str(e)}") - print("Full traceback:") - print(traceback.format_exc()) - return 0 - - -def main(): - parser = setup_argument_parser() - try: - args = parser.parse_args() - validate_squash_script(args.squash_script_path) - pr_data_json = json.loads(args.pr_data) - - # Process the PRs - success_count = process_pr(pr_data_json, args.source_branch, args.target_branch, args.squash_script_path) - print(f"Successfully processed {success_count} PRs") - - except Exception as e: - print(f"Fatal error: {str(e)}", file=sys.stderr) - return 1 + return success_count + except Exception as e: + import traceback + print(f"Error in process_pr: {str(e)}") + print("Full traceback:") + print(traceback.format_exc()) return 0 +def main(): + parser = setup_argument_parser() + try: + args = parser.parse_args() + validate_squash_script(args.squash_script_path) + pr_data_json = json.loads(args.pr_data) + + # Process the PRs + success_count = process_pr(pr_data_json, args.source_branch, args.target_branch, args.squash_script_path) + print(f"Successfully processed {success_count} PRs") + + except Exception as e: + print(f"Fatal error: {str(e)}", file=sys.stderr) + return 1 + + return 0 + + if __name__ == "__main__": - sys.exit(main()) + sys.exit(main())