diff --git a/scripts/guru-sync-issues b/scripts/guru-sync-issues deleted file mode 100755 index 10b397a..0000000 --- a/scripts/guru-sync-issues +++ /dev/null @@ -1,271 +0,0 @@ -#! /usr/bin/env python3 -import argparse -import sys -import logging -import requests -import subprocess -from textwrap import dedent -from pathlib import Path -from datetime import datetime -from typing import Optional - -try: - import gitlab -except ImportError: - print("Please install the 'python-gitlab' package from pip", file=sys.stderr) - sys.exit(1) - -TRELLO_KEY = "fba640a85f15c91e93e6b3f88e59489c" -TRELLO_BOARD_ID = "tw3Cn3L6" -TRELLO_LIST_ID = "6593166e9dd338621ed6848d" -TRELLO_TAGS = { - "company": "64e03ac77d27032282436d28", # Tag used to sort by company - "waiting_to_merge": "65524315edf2d2edb0cc5d09", # Tag used to indicate a MR is waiting to merge - "draft": "65fdd81c83e5d6e00f1b9721", # Tag used to indicate a draft MR -} -GITLAB_ENDPOINT = "http://gitlab.guru-domain.gurustudio.com" -MY_ID = 64 - -logger = logging.getLogger(__name__) - - -def main() -> int: - # Handle program arguments - ap = argparse.ArgumentParser( - prog="guru-sync-issues", description="Sync issues from GitLab to Trello" - ) - ap.add_argument( - "-v", "--verbose", help="Enable verbose logging", action="store_true" - ) - args = ap.parse_args() - - # Configure logging - logging.basicConfig( - level=logging.DEBUG if args.verbose else logging.INFO, - format="%(levelname)s: %(message)s", - ) - - # Call `ewp-secrets` to obtain the GitLab token - secrets_proc = subprocess.run( - [ - (Path(__file__).parent / "ewp-secrets").as_posix(), - "load", - "-n", - "gurustudio", - "-k", - "gitlab-pat", - ], - capture_output=True, - ) - - # If the secret manager failed, exit - if secrets_proc.returncode != 0: - print("Failed to load GitLab PAT", file=sys.stderr) - print( - "Please run `ewp-secrets store -n gurustudio -k gitlab-pat` to set a token", - file=sys.stderr, - ) - return 1 - - # Extract the GitLab token - gitlab_token = secrets_proc.stdout.decode().strip() - - # Call `ewp-secrets` to obtain the Trello API token - secrets_proc = subprocess.run( - [ - (Path(__file__).parent / "ewp-secrets").as_posix(), - "load", - "-n", - "ewpratten", - "-k", - "trello-api-token", - ], - capture_output=True, - ) - - # If the secret manager failed, exit - if secrets_proc.returncode != 0: - print("Failed to load Trello API token", file=sys.stderr) - print( - "Please run `ewp-secrets store -n ewpratten -k trello-api-token` to set a token", - file=sys.stderr, - ) - return 1 - - # Extract the Trello API token - trello_api_token = secrets_proc.stdout.decode().strip() - - # Try to ping the gitlab server, and exit if it fails - try: - logger.debug("Attempting to connect to GitLab server") - requests.get(GITLAB_ENDPOINT, timeout=2) - except requests.exceptions.ConnectionError: - logger.error("Could not connect to GitLab server") - return 1 - - # Authenticate with GitLab - gitlab_client = gitlab.Gitlab( - GITLAB_ENDPOINT, private_token=gitlab_token, user_agent="guru-sync-issues" - ) - - # Access the main group - pp_group = gitlab_client.groups.get("pipeline-planning") - - # Find all open issues I'm assigned to - open_issues = [] - open_issues.extend( - gitlab_client.issues.list(state="opened", get_all=True, assignee_id=MY_ID) - ) - open_issues.extend( - pp_group.issues.list(state="opened", get_all=True, assignee_id=MY_ID) - ) - - # De-dupe the issues - open_issues = list(set(open_issues)) - - # Find all open MRs I'm assigned to - open_mrs = [] - open_mrs.extend( - gitlab_client.mergerequests.list( - state="opened", get_all=True, assignee_id=MY_ID - ) - ) - open_mrs.extend( - pp_group.mergerequests.list(state="opened", get_all=True, assignee_id=MY_ID) - ) - - # De-dupe the MRs - open_mrs = list(set(open_mrs)) - - # Log findings - logger.info(f"Found {len(open_issues)} open issues") - logger.info(f"Found {len(open_mrs)} open merge requests") - - # Get all cards in Trello - response = requests.get( - f"https://api.trello.com/1/boards/{TRELLO_BOARD_ID}/cards", - params={ - "key": TRELLO_KEY, - "token": trello_api_token, - }, - ) - response.raise_for_status() - trello_cards = response.json() - - # Iterate over each GitLab actionable - actionables = [("issue", issue) for issue in open_issues] + [ - ("merge request", mr) for mr in open_mrs - ] - for ty, issue in actionables: - # Get the URL of the issue - issue_url = issue.web_url - - # Check if there is a card that references this URL - card = next((c for c in trello_cards if issue_url in c["desc"]), None) - - # If none exists, make a new card - if not card: - logger.info(f"Creating card for issue: {issue.title}") - - # Build params - card_params = { - "idList": TRELLO_LIST_ID, - "name": issue.title, - "key": TRELLO_KEY, - "token": trello_api_token, - "pos": "top", - "desc": dedent( - f""" - ## Linked GitLab Issues - - {issue_url} - - --- - """ - ), - "idLabels": TRELLO_TAGS["company"], - } - - # Make the card - response = requests.post( - "https://api.trello.com/1/cards", - params=card_params, - ) - response.raise_for_status() - - # Capture the card for later - card = response.json() - logger.info(f"Created card: {card['id']}") - - # Apply lables to MRs - if ty == "merge request": - if issue.title.startswith("Draft:") or issue.title.startswith("WIP:"): - # Check if the card already has the 'Draft' label - if any( - label["id"] == TRELLO_TAGS["draft"] for label in card["labels"] - ): - continue - - logger.info(f"Adding 'Draft' label to card {card['id']}") - response = requests.post( - f"https://api.trello.com/1/cards/{card['id']}/idLabels", - params={ - "key": TRELLO_KEY, - "token": trello_api_token, - "value": TRELLO_TAGS["draft"], - }, - ) - response.raise_for_status() - - else: - # Check if the card already has the 'Waiting to Merge' label - if any( - label["id"] == TRELLO_TAGS["waiting_to_merge"] for label in card["labels"] - ): - continue - - logger.info(f"Adding 'Waiting to Merge' label to card {card['id']}") - response = requests.post( - f"https://api.trello.com/1/cards/{card['id']}/idLabels", - params={ - "key": TRELLO_KEY, - "token": trello_api_token, - "value": TRELLO_TAGS["waiting_to_merge"], - }, - ) - response.raise_for_status() - - # Determine the due date assigned in GitLab - if ty != "merge request": - due_date: Optional[str] = issue.due_date - if due_date: - # Check if the card already has a due date - card_due_date = card.get("due", None) - if card_due_date: - card_due_date = card_due_date.split("T")[0] - - # Convert to a useful format - due_date = datetime.strptime(due_date, "%Y-%m-%d") - card_due_date = datetime.strptime(card_due_date, "%Y-%m-%d") if card_due_date else None - - # If the card has an earlier due date, skip - if card_due_date and due_date < card_due_date: - logger.debug(f"Skipping due date update for {card['id']} because it has an earlier due date already") - continue - - # Update the due date - logger.info(f"Updating due date for card {card['id']} to {due_date}") - response = requests.put( - f"https://api.trello.com/1/cards/{card['id']}", - params={ - "key": TRELLO_KEY, - "token": trello_api_token, - "due": due_date.isoformat(), - }, - ) - - - return 0 - - -if __name__ == "__main__": - sys.exit(main())