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
|
#! /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 argparse
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
import subprocess
|
||||||
import requests
|
import requests
|
||||||
from pathlib import Path
|
|
||||||
from dataclasses import dataclass, field
|
try:
|
||||||
from typing import List, Optional, Dict, Any
|
import github
|
||||||
from ewconfig.secret_manager import get_semi_secret_string
|
except ImportError:
|
||||||
from ewconfig.trello import TRELLO_API_KEY, get_trello_api_token
|
print("Please install the 'PyGithub' package from pip", file=sys.stderr)
|
||||||
from ewconfig.trello.cards import get_all_trello_cards, create_card, add_attachment
|
sys.exit(1)
|
||||||
from ewconfig.trello.boards import PERSONAL_TASKS_BOARD
|
|
||||||
|
TRELLO_BOARD_ID = "tw3Cn3L6"
|
||||||
|
TRELLO_LIST_ID = "6348a3ce5208f505b61d29bf" # To Do
|
||||||
|
TRELLO_TAGS = {
|
||||||
|
"waiting_to_merge": "65524315edf2d2edb0cc5d09",
|
||||||
|
"draft": "65fdd81c83e5d6e00f1b9721",
|
||||||
|
"issue": "64eb5d72fb694cd8f0ba7a8d"
|
||||||
|
}
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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:
|
def main() -> int:
|
||||||
# Handle program arguments
|
# Handle program arguments
|
||||||
ap = argparse.ArgumentParser(prog="", description="")
|
ap = argparse.ArgumentParser(
|
||||||
ap.add_argument("--dry-run", help="Don't actually do anything", action="store_true")
|
prog="github-to-trello", description="Syncs GitHub issues to Trello cards"
|
||||||
|
)
|
||||||
|
|
||||||
ap.add_argument(
|
ap.add_argument(
|
||||||
"-v", "--verbose", help="Enable verbose logging", action="store_true"
|
"-v", "--verbose", help="Enable verbose logging", action="store_true"
|
||||||
)
|
)
|
||||||
@ -102,72 +39,94 @@ def main() -> int:
|
|||||||
format="%(levelname)s: %(message)s",
|
format="%(levelname)s: %(message)s",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get a list of all issues assigned to me
|
# Read Trello API credentials
|
||||||
my_issues = get_all_issues()
|
trello_api_key = subprocess.check_output(
|
||||||
logger.info(f"Found {len(my_issues)} issues assigned to me")
|
"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
|
# Read GitHub credential
|
||||||
trello_cards = get_all_trello_cards(
|
logger.info("Authenticating with GitHub")
|
||||||
board_id=PERSONAL_TASKS_BOARD.id,
|
github_pat = subprocess.check_output(
|
||||||
api_key=TRELLO_API_KEY,
|
"op read -n 'op://ewconfig/obs3gaeg7lcff7v5ewbvgxvwgu/credential'", shell=True
|
||||||
api_token=TRELLO_API_TOKEN,
|
).decode()
|
||||||
)
|
gh_api = github.Github(auth=github.Auth.Token(github_pat))
|
||||||
logger.info(f"Found {len(trello_cards)} cards in Trello")
|
|
||||||
|
|
||||||
# Handle each GitHub issue
|
# Get my user object
|
||||||
for issue in my_issues:
|
user = gh_api.get_user()
|
||||||
# Ignore archived repos
|
logger.info(f"Authenticated as {user.login}")
|
||||||
if issue["repository"]["archived"]:
|
|
||||||
logger.info(f"Ignoring archived repo: {issue['repository']['full_name']}")
|
# Get all my issues
|
||||||
continue
|
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
|
# Check if any trello card already mentions this issue in the description
|
||||||
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
|
|
||||||
for card in trello_cards:
|
for card in trello_cards:
|
||||||
if issue["html_url"] in card["desc"]:
|
if issue.html_url in card["desc"]:
|
||||||
logger.info(
|
logger.info(f"Found Trello card {card['id']} for this issue")
|
||||||
f"Found GitHub Issue {issue['number']} in Trello Card {card['id']}"
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(f"Creating Trello card for this issue")
|
||||||
f"Going to create trello card for GitHub Issue: [{issue['repository']['full_name']}] {issue['title']}"
|
|
||||||
|
# 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:
|
logger.info(f"Attached issue to Trello card {card['id']}")
|
||||||
# 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']}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user