github/backport-active/backport_script.py (101 lines of code) (raw):

import os import re import json import requests def main(): # Get environment variables github_token = os.environ['GITHUB_TOKEN'] pr_number = int(os.environ['PR_NUMBER']) repository = os.environ['REPOSITORY'] labels_json = os.environ['PR_LABELS'] backports_url = os.environ['BACKPORTS_URL'] print(f"Using backports URL: {backports_url}") # Define GitHub API headers headers = { 'Authorization': f'token {github_token}', 'Accept': 'application/vnd.github.v3+json' } labels = [] labels_json = os.environ.get('PR_LABELS', '[]') # If we have some JSON data, try to extract label names if labels_json and labels_json != 'null': try: labels_data = json.loads(labels_json) if isinstance(labels_data, list): for item in labels_data: if isinstance(item, dict) and 'name' in item: labels.append(item['name']) except json.JSONDecodeError: print(f"Invalid JSON in PR_LABELS: {labels_json}") # If no labels found, fetch from GitHub API (needed for non PR events) if not labels: try: url = f"https://api.github.com/repos/{repository}/issues/{pr_number}/labels" response = requests.get(url, headers=headers) if response.status_code == 200: api_labels = response.json() for item in api_labels: if isinstance(item, dict) and 'name' in item: labels.append(item['name']) else: print(f"API returned status code: {response.status_code}") except Exception as e: print(f"Error fetching labels {str(e)}") print(f"Labels found: {labels}") # Function to get config from URL def get_config_from_url(url): try: print(f"Fetching config from URL: {url}") response = requests.get(url, timeout=10) response.raise_for_status() return response.json() except Exception as e: print(f"Error fetching config from URL: {e}") return None # Always fetch from the backports URL config = get_config_from_url(backports_url) target_branches = [] # Extract branches from config if available if config and 'branches' in config: branches_data = config['branches'] if isinstance(branches_data, str): # If branches is a string with space-separated values, split it target_branches = [b for b in branches_data.split() if b] elif isinstance(branches_data, list): # If branches is an array, use it directly target_branches = branches_data print(f"Found branches in config: {target_branches}") else: print("No branches found in config or config not available") # Filter branches based on the labels filtered_branches = [] if 'backport-active-all' in labels: filtered_branches = [branch for branch in target_branches if branch != 'main' and branch != '7.17'] print('Using all branches from JSON (excluding main) due to backport-active-all label') else: # Process 8.x and 9.x branches based on labels if 'backport-active-8' in labels: branches8 = [branch for branch in target_branches if branch.startswith('8.') or branch == '8.x' or branch == '8'] filtered_branches.extend(branches8) print(f"Found {len(branches8)} 8.x branches to backport") if 'backport-active-9' in labels: branches9 = [branch for branch in target_branches if branch.startswith('9.') or branch == '9.x' or branch == '9'] filtered_branches.extend(branches9) print(f"Found {len(branches9)} 9.x branches to backport") # Remove duplicates while preserving order seen = set() filtered_branches = [x for x in filtered_branches if not (x in seen or seen.add(x))] print(f"Final branches for backporting: {filtered_branches}") # Add comment to the PR if we have branches to backport to success = False if filtered_branches: comment = f"@mergifyio backport {' '.join(filtered_branches)}" # Create comment via GitHub API comment_url = f"https://api.github.com/repos/{repository}/issues/{pr_number}/comments" comment_data = {"body": comment} print(f'COMMENT_URL = {comment_url}') print(f'COMMENT_DATA = {comment_data}') print("Using GitHub token for authentication - comment will be posted") response = requests.post(comment_url, headers=headers, json=comment_data) if response.status_code == 201: print(f"Successfully added backport comment: {comment}") success = True else: print(f"Failed to add comment. Status code: {response.status_code}") print(f"Response: {response.text}") success = False else: print("No branches to backport to after filtering") success = True # No branches to backport is not an error condition return 0 if success else 1 if __name__ == "__main__": exit_code = main() exit(exit_code)