-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/issue-to-pr/issue-to-pr int…
…o nikita
- Loading branch information
Showing
8 changed files
with
344 additions
and
185 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,5 @@ __pycache__ | |
# Virtual Environment | ||
venv | ||
|
||
venv/ | ||
venv* | ||
.venv | ||
/venv | ||
/venv/ | ||
# Others | ||
.aider.tags.cache.v3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,64 @@ | ||
import datetime | ||
# Standard imports | ||
import hashlib # For HMAC (Hash-based Message Authentication Code) signatures | ||
import hmac # For HMAC (Hash-based Message Authentication Code) signatures | ||
import jwt # For generating JWTs (JSON Web Tokens) | ||
import logging | ||
import requests | ||
import time | ||
|
||
# Third-party imports | ||
from fastapi import Request | ||
import jwt # For generating JWTs (JSON Web Tokens) | ||
|
||
|
||
class GitHubManager: | ||
def __init__(self, app_identifier, private_key): | ||
self.app_identifier = app_identifier | ||
self.private_key = private_key | ||
# Constructor to initialize the GitHub App ID and private key to this instance | ||
def __init__(self, app_id: str, private_key: bytes) -> None: | ||
self.app_id: str = app_id | ||
self.private_key: bytes = private_key | ||
|
||
# Generate a JWT (JSON Web Token) for GitHub App authentication | ||
def create_jwt(self): | ||
now = int(datetime.datetime.utcnow().timestamp()) | ||
payload = { | ||
def create_jwt(self) -> str: | ||
now = int(time.time()) | ||
payload: dict[str, int | str] = { | ||
"iat": now, # Issued at time | ||
"exp": now + (10 * 60), # JWT expires in 10 minutes | ||
"iss": self.app_identifier, # Issuer | ||
"sub": self.app_identifier # Subject | ||
"exp": now + 600, # JWT expires in 10 minutes | ||
"iss": self.app_id, # Issuer | ||
} | ||
# The reason we use RS256 is that GitHub requires it for JWTs | ||
return jwt.encode(payload, self.private_key, algorithm="RS256") | ||
return jwt.encode(payload=payload, key=self.private_key, algorithm="RS256") | ||
|
||
# Verify the webhook signature for security | ||
async def verify_webhook_signature(self, request, secret): | ||
signature = request.headers.get("X-Hub-Signature-256") | ||
body = await request.body() | ||
async def verify_webhook_signature(self, request: Request, secret: str) -> None: | ||
signature: str | None = request.headers.get("X-Hub-Signature-256") | ||
if signature is None: | ||
raise ValueError("Missing webhook signature") | ||
body: bytes = await request.body() | ||
|
||
# Compare the computed signature with the one in the headers | ||
expected_signature = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() | ||
hmac_key: bytes = secret.encode() | ||
hmac_signature: str = hmac.new(key=hmac_key, msg=body, digestmod=hashlib.sha256).hexdigest() | ||
expected_signature: str = "sha256=" + hmac_signature | ||
if not hmac.compare_digest(signature, expected_signature): | ||
raise ValueError("Invalid webhook signature") | ||
|
||
# Get an access token for the installed GitHub App | ||
def get_installation_access_token(self, installation_id): | ||
jwt_token = self.create_jwt() | ||
headers = { | ||
"Authorization": f"Bearer {jwt_token}", | ||
"Accept": "application/vnd.github.v3+json" | ||
} | ||
url = f"https://api.github.com/app/installations/{installation_id}/access_tokens" | ||
response = requests.post(url, headers=headers) | ||
response.raise_for_status() # Raises HTTPError for bad responses | ||
return response.json()["token"] | ||
def get_installation_access_token(self, installation_id: int) -> tuple[str, str]: | ||
try: | ||
jwt_token: str = self.create_jwt() | ||
headers: dict[str, str] = { | ||
"Authorization": f"Bearer {jwt_token}", | ||
"Accept": "application/vnd.github.v3+json", | ||
"X-GitHub-Api-Version": "2022-11-28" | ||
} | ||
url: str = f"https://api.github.com/app/installations/{installation_id}/access_tokens" | ||
|
||
response = requests.post(url=url, headers=headers) | ||
response.raise_for_status() # Raises HTTPError for bad responses | ||
json = response.json() | ||
return json["token"], json["expires_at"] | ||
except requests.exceptions.HTTPError as e: | ||
logging.error(msg=f"HTTP Error: {e.response.status_code} - {e.response.text}") | ||
raise | ||
except Exception as e: | ||
logging.error(msg=f"Error: {e}") | ||
raise |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
from typing import TypedDict, Dict, List, Optional | ||
|
||
|
||
class LabelInfo(TypedDict): | ||
id: int | ||
node_id: str | ||
url: str | ||
name: str | ||
color: str | ||
default: bool | ||
description: Optional[str] | ||
|
||
|
||
class UserInfo(TypedDict): | ||
login: str | ||
id: int | ||
node_id: str | ||
avatar_url: str | ||
gravatar_id: str | ||
url: str | ||
html_url: str | ||
followers_url: str | ||
following_url: str | ||
gists_url: str | ||
starred_url: str | ||
subscriptions_url: str | ||
organizations_url: str | ||
repos_url: str | ||
events_url: str | ||
received_events_url: str | ||
type: str | ||
site_admin: bool | ||
|
||
|
||
class IssueInfo(TypedDict): | ||
url: str | ||
repository_url: str | ||
labels_url: str | ||
comments_url: str | ||
events_url: str | ||
html_url: str | ||
id: int | ||
node_id: str | ||
number: int | ||
title: str | ||
user: UserInfo | ||
labels: List[LabelInfo] | ||
state: str | ||
locked: bool | ||
assignee: Optional[UserInfo] | ||
assignees: List[UserInfo] | ||
milestone: Optional[str] | ||
comments: int | ||
created_at: str | ||
updated_at: str | ||
closed_at: Optional[str] | ||
author_association: str | ||
active_lock_reason: Optional[str] | ||
body: Optional[str] | ||
reactions: Dict[str, int] | ||
timeline_url: str | ||
performed_via_github_app: Optional[str] | ||
state_reason: Optional[str] | ||
|
||
|
||
class OrganizationInfo(TypedDict): | ||
login: str | ||
id: int | ||
node_id: str | ||
url: str | ||
repos_url: str | ||
events_url: str | ||
hooks_url: str | ||
issues_url: str | ||
members_url: str | ||
public_members_url: str | ||
avatar_url: str | ||
description: Optional[str] | ||
|
||
|
||
class RepositoryInfo(TypedDict): | ||
id: int | ||
node_id: str | ||
name: str | ||
full_name: str | ||
private: bool | ||
|
||
|
||
class PermissionsInfo(TypedDict): | ||
actions: str | ||
contents: str | ||
metadata: str | ||
workflows: str | ||
repository_hooks: str | ||
|
||
|
||
class InstallationInfo(TypedDict): | ||
id: int | ||
account: UserInfo | ||
repository_selection: str | ||
access_tokens_url: str | ||
repositories_url: str | ||
html_url: str | ||
app_id: int | ||
app_slug: str | ||
target_id: int | ||
target_type: str | ||
permissions: PermissionsInfo | ||
events: List[str] | ||
created_at: str | ||
updated_at: str | ||
single_file_name: Optional[str] | ||
has_multiple_single_files: bool | ||
single_file_paths: List[str] | ||
suspended_by: Optional[str] | ||
suspended_at: Optional[str] | ||
|
||
|
||
class GitHubInstallationPayload(TypedDict): | ||
action: str | ||
installation: InstallationInfo | ||
repositories: List[RepositoryInfo] | ||
repository_selection: str | ||
repositories_added: List[RepositoryInfo] | ||
repositories_removed: List[RepositoryInfo] | ||
requester: Optional[UserInfo] | ||
sender: UserInfo | ||
|
||
|
||
class GitHubLabeledPayload(TypedDict): | ||
action: str | ||
issue: IssueInfo | ||
label: LabelInfo | ||
repository: RepositoryInfo | ||
organization: OrganizationInfo | ||
sender: UserInfo |
Oops, something went wrong.