#! /usr/bin/env python3 import argparse import sys import logging import subprocess import requests try: import github except ImportError: print("Please install the 'PyGithub' package from pip", file=sys.stderr) sys.exit(1) TRELLO_BOARD_ID = "tw3Cn3L6" TRELLO_LIST_ID = "6348a3ce5208f505b61d29bf" # To Do TRELLO_TAGS = { "waiting_to_merge": "65524315edf2d2edb0cc5d09", "draft": "65fdd81c83e5d6e00f1b9721", "issue": "64eb5d72fb694cd8f0ba7a8d" } logger = logging.getLogger(__name__) def main() -> int: # Handle program arguments ap = argparse.ArgumentParser( prog="github-to-trello", description="Syncs GitHub issues to Trello cards" ) 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", ) # Read Trello API credentials trello_api_key = subprocess.check_output( "op read -n 'op://ewconfig/cbnd5vv3germmc4korkxx3nsim/api key'", shell=True ).decode() trello_api_token = subprocess.check_output( "op read -n 'op://ewconfig/cbnd5vv3germmc4korkxx3nsim/credential'", shell=True, ).decode() # Read GitHub credential logger.info("Authenticating with GitHub") github_pat = subprocess.check_output( "op read -n 'op://ewconfig/obs3gaeg7lcff7v5ewbvgxvwgu/credential'", shell=True ).decode() gh_api = github.Github(auth=github.Auth.Token(github_pat)) # Get my user object user = gh_api.get_user() logger.info(f"Authenticated as {user.login}") # Get all my issues issues = list(user.get_issues()) logger.info(f"Found {len(issues)} issues/prs") # Filter out any issue that is in an archived repo issues = [issue for issue in issues if not issue.repository.archived] logger.info(f"{len(issues)} of those are actually in active repos") # Fetch a list of all of my Trello cards trello_cards = requests.get( f"https://api.trello.com/1/boards/{TRELLO_BOARD_ID}/cards", params={"key": trello_api_key, "token": trello_api_token}, ).json() for issue in issues: repo = issue.repository is_pr = issue.pull_request is not None author = issue.user.login logger.info( f"Found {'pr' if is_pr else 'issue'} {repo.full_name}#{issue.number} by {author}" ) # Check if any trello card already mentions this issue in the description for card in trello_cards: if issue.html_url in card["desc"]: logger.info(f"Found Trello card {card['id']} for this issue") break else: logger.info(f"Creating Trello card for this issue") # Sort out the appropriate labels card_labels = [] if is_pr: # If this is a draft PR, add the draft label if issue.draft: card_labels.append(TRELLO_TAGS["draft"]) else: # Otherwise, add the waiting to merge label card_labels.append(TRELLO_TAGS["waiting_to_merge"]) else: # If this is an issue, add the issue label card_labels.append(TRELLO_TAGS["issue"]) print(card_labels) # Create the card card = requests.post( f"https://api.trello.com/1/cards", params={ "key": trello_api_key, "token": trello_api_token, "idList": TRELLO_LIST_ID, "name": f"[{repo.full_name}] {issue.title}", "desc": f"**GitHub Link:** {issue.html_url}\n\n**Author:** [{author}]({issue.user.html_url})\n\n---", "idLabels": card_labels }, ).json() logger.info(f"Created Trello card {card['id']} for this issue") # Attach the issue to the card requests.post( f"https://api.trello.com/1/cards/{card['id']}/attachments", params={ "key": trello_api_key, "token": trello_api_token, "url": issue.html_url } ) logger.info(f"Attached issue to Trello card {card['id']}") return 0 if __name__ == "__main__": sys.exit(main())