Clean out old Trello code
This commit is contained in:
parent
ca1b45019d
commit
ffcf88cb16
@ -1,24 +0,0 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
SEMI_SECRET_BASE_PATH = Path("~/.config/ewconfig/secrets/semi-secret").expanduser()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_semi_secret_string(name: str, namespace: Optional[str] = None) -> str:
|
||||
logger.debug(f"Attempting to load secret: {name} (ns: {namespace})")
|
||||
|
||||
# Construct file path
|
||||
file = SEMI_SECRET_BASE_PATH
|
||||
if namespace:
|
||||
file = file / namespace
|
||||
file = file / name
|
||||
|
||||
# Make sure it exists
|
||||
if not file.exists():
|
||||
raise FileNotFoundError(f"Could not load secret from: {file}")
|
||||
|
||||
# Read the value
|
||||
with open(file, "r") as f:
|
||||
return f.read().strip()
|
@ -1,8 +0,0 @@
|
||||
from ..secret_manager import get_semi_secret_string
|
||||
|
||||
TRELLO_API_KEY = "fba640a85f15c91e93e6b3f88e59489c"
|
||||
"""Public api key to do things to personal Trello"""
|
||||
|
||||
|
||||
def get_trello_api_token() -> str:
|
||||
return get_semi_secret_string("trello_api_token")
|
@ -1,20 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict
|
||||
|
||||
|
||||
@dataclass
|
||||
class TrelloBoardInfo:
|
||||
id: str
|
||||
lists: Dict[str, str]
|
||||
tags: Dict[str, str]
|
||||
|
||||
|
||||
PERSONAL_TASKS_BOARD = TrelloBoardInfo(
|
||||
id="tw3Cn3L6",
|
||||
lists={"To Do": "6348a3ce5208f505b61d29bf"},
|
||||
tags={
|
||||
"GURU": "64e03ac77d27032282436d28",
|
||||
"Github: Issue": "64eb5d72fb694cd8f0ba7a8d",
|
||||
"Github: Pull Request": "652d4b775f5c59a8e6308216",
|
||||
},
|
||||
)
|
@ -1,80 +0,0 @@
|
||||
import requests
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_all_trello_cards(
|
||||
board_id: str, api_key: str, api_token: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
# Get a list of cards on the board
|
||||
logger.debug(f"Getting all cards on board: {board_id}")
|
||||
response = requests.get(
|
||||
f"https://api.trello.com/1/boards/{board_id}/cards",
|
||||
params={
|
||||
"key": api_key,
|
||||
"token": api_token,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
cards = response.json()
|
||||
logger.debug(f"Found {len(cards)} cards on board: {board_id}")
|
||||
return cards
|
||||
|
||||
|
||||
def create_card(
|
||||
list_id: str,
|
||||
name: str,
|
||||
api_key: str,
|
||||
api_token: str,
|
||||
description: Optional[str] = None,
|
||||
label_ids: Optional[List[str]] = None,
|
||||
position: str = "top",
|
||||
) -> str:
|
||||
logger.debug(f"Creating card: {name}")
|
||||
|
||||
# Build out params
|
||||
params = {
|
||||
"idList": list_id,
|
||||
"name": name,
|
||||
"key": api_key,
|
||||
"token": api_token,
|
||||
"pos": position,
|
||||
}
|
||||
if description:
|
||||
params["desc"] = description
|
||||
if label_ids:
|
||||
params["idLabels"] = ",".join(label_ids)
|
||||
|
||||
# Make a new card
|
||||
response = requests.post(
|
||||
"https://api.trello.com/1/cards",
|
||||
params=params,
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# Get the new card's id
|
||||
card_id = response.json()["id"]
|
||||
|
||||
logger.debug(f"Created card: {card_id}")
|
||||
return card_id
|
||||
|
||||
|
||||
def add_attachment(
|
||||
card_id: str, api_key: str, api_token: str, url: Optional[str] = None
|
||||
) -> None:
|
||||
logger.debug(f"Adding attachment to card: {card_id}")
|
||||
params = {
|
||||
"key": api_key,
|
||||
"token": api_token,
|
||||
}
|
||||
if url:
|
||||
params["url"] = url
|
||||
|
||||
response = requests.post(
|
||||
f"https://api.trello.com/1/cards/{card_id}/attachments",
|
||||
params=params,
|
||||
)
|
||||
response.raise_for_status()
|
||||
logger.debug(f"Added attachment to card: {card_id}")
|
@ -1,96 +1,33 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
# fmt:off
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
sys.path.append((Path(os.environ["EWCONFIG_ROOT"]) / "python_modules").as_posix())
|
||||
# fmt:on
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import logging
|
||||
import subprocess
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional, Dict, Any
|
||||
from ewconfig.secret_manager import get_semi_secret_string
|
||||
from ewconfig.trello import TRELLO_API_KEY, get_trello_api_token
|
||||
from ewconfig.trello.cards import get_all_trello_cards, create_card, add_attachment
|
||||
from ewconfig.trello.boards import PERSONAL_TASKS_BOARD
|
||||
|
||||
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__)
|
||||
|
||||
GITHUB_API_VERSION = "2022-11-28"
|
||||
GITHUB_PAT = get_semi_secret_string("github_pat", namespace="trello-sync")
|
||||
TRELLO_API_TOKEN = get_trello_api_token()
|
||||
|
||||
|
||||
def get_all_issues() -> List[Dict[str, Any]]:
|
||||
issues = []
|
||||
|
||||
# Get all issues assigned to me
|
||||
response = requests.get(
|
||||
"https://api.github.com/issues",
|
||||
headers={
|
||||
"Authorization": f"token {GITHUB_PAT}",
|
||||
"Accept": "application/vnd.github.raw+json",
|
||||
"X-GitHub-Api-Version": GITHUB_API_VERSION,
|
||||
},
|
||||
params={"state": "open", "per_page": 100},
|
||||
)
|
||||
response.raise_for_status()
|
||||
issues.extend(response.json())
|
||||
|
||||
# Get all issues that mention me
|
||||
response = requests.get(
|
||||
"https://api.github.com/user/issues",
|
||||
headers={
|
||||
"Authorization": f"token {GITHUB_PAT}",
|
||||
"Accept": "application/vnd.github.raw+json",
|
||||
"X-GitHub-Api-Version": GITHUB_API_VERSION,
|
||||
},
|
||||
params={"state": "open", "per_page": 100, "filter": "mentioned"},
|
||||
)
|
||||
response.raise_for_status()
|
||||
issues.extend(response.json())
|
||||
|
||||
# Get all issues that exist in my repos
|
||||
response = requests.get(
|
||||
"https://api.github.com/user/issues",
|
||||
headers={
|
||||
"Authorization": f"token {GITHUB_PAT}",
|
||||
"Accept": "application/vnd.github.raw+json",
|
||||
"X-GitHub-Api-Version": GITHUB_API_VERSION,
|
||||
},
|
||||
params={"state": "open", "per_page": 100, "filter": "repos"},
|
||||
)
|
||||
response.raise_for_status()
|
||||
issues.extend(response.json())
|
||||
|
||||
# Get all issues that I have made in other people's repos
|
||||
response = requests.get(
|
||||
"https://api.github.com/user/issues",
|
||||
headers={
|
||||
"Authorization": f"token {GITHUB_PAT}",
|
||||
"Accept": "application/vnd.github.raw+json",
|
||||
"X-GitHub-Api-Version": GITHUB_API_VERSION,
|
||||
},
|
||||
params={"state": "open", "per_page": 100, "filter": "subscribed"},
|
||||
)
|
||||
response.raise_for_status()
|
||||
issues.extend(response.json())
|
||||
|
||||
# De-dupe issues
|
||||
issues = list({issue["id"]: issue for issue in issues}.values())
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def main() -> int:
|
||||
# Handle program arguments
|
||||
ap = argparse.ArgumentParser(prog="", description="")
|
||||
ap.add_argument("--dry-run", help="Don't actually do anything", action="store_true")
|
||||
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"
|
||||
)
|
||||
@ -102,72 +39,94 @@ def main() -> int:
|
||||
format="%(levelname)s: %(message)s",
|
||||
)
|
||||
|
||||
# Get a list of all issues assigned to me
|
||||
my_issues = get_all_issues()
|
||||
logger.info(f"Found {len(my_issues)} issues assigned to me")
|
||||
# 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()
|
||||
|
||||
# Get all cards on the personal tasks board
|
||||
trello_cards = get_all_trello_cards(
|
||||
board_id=PERSONAL_TASKS_BOARD.id,
|
||||
api_key=TRELLO_API_KEY,
|
||||
api_token=TRELLO_API_TOKEN,
|
||||
)
|
||||
logger.info(f"Found {len(trello_cards)} cards in Trello")
|
||||
# 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))
|
||||
|
||||
# Handle each GitHub issue
|
||||
for issue in my_issues:
|
||||
# Ignore archived repos
|
||||
if issue["repository"]["archived"]:
|
||||
logger.info(f"Ignoring archived repo: {issue['repository']['full_name']}")
|
||||
continue
|
||||
# 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}"
|
||||
)
|
||||
|
||||
# Ignore anything by dependabot
|
||||
if issue["user"]["login"] == "dependabot[bot]":
|
||||
logger.debug(f"Ignoring dependabot issue: {issue['repository']['full_name']}#{issue['number']}")
|
||||
continue
|
||||
|
||||
# Search each card for anything that links to the github issue
|
||||
# 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 GitHub Issue {issue['number']} in Trello Card {card['id']}"
|
||||
)
|
||||
if issue.html_url in card["desc"]:
|
||||
logger.info(f"Found Trello card {card['id']} for this issue")
|
||||
break
|
||||
else:
|
||||
logger.info(
|
||||
f"Going to create trello card for GitHub Issue: [{issue['repository']['full_name']}] {issue['title']}"
|
||||
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
|
||||
}
|
||||
)
|
||||
if not args.dry_run:
|
||||
# Check if this is an issue or pr
|
||||
is_pr = "pull_request" in issue
|
||||
type_label = (
|
||||
PERSONAL_TASKS_BOARD.tags["Github: Pull Request"]
|
||||
if is_pr
|
||||
else PERSONAL_TASKS_BOARD.tags["Github: Issue"]
|
||||
)
|
||||
|
||||
# Create a new trello card for this issue
|
||||
card_id = create_card(
|
||||
list_id=PERSONAL_TASKS_BOARD.lists["To Do"],
|
||||
name=f"[{issue['repository']['full_name']}] {issue['title']}",
|
||||
description=(
|
||||
f"**GitHub Link:** [`{issue['repository']['full_name']}#{issue['number']}`]({issue['html_url']})\n\n"
|
||||
f"**Author:** [`{issue['user']['login']}`]({issue['user']['html_url']})\n\n"
|
||||
"---"
|
||||
),
|
||||
label_ids=[type_label],
|
||||
api_key=TRELLO_API_KEY,
|
||||
api_token=TRELLO_API_TOKEN,
|
||||
)
|
||||
add_attachment(
|
||||
card_id=card_id,
|
||||
api_key=TRELLO_API_KEY,
|
||||
api_token=TRELLO_API_TOKEN,
|
||||
url=issue["html_url"],
|
||||
)
|
||||
logger.info(
|
||||
f"Created Trello Card {card_id} for GitHub Issue {issue['repository']['full_name']}#{issue['number']}"
|
||||
)
|
||||
logger.info(f"Attached issue to Trello card {card['id']}")
|
||||
|
||||
return 0
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user