goose/.github/scripts/send_key.py
Angie Jones 2032c7099c
Some checks failed
CI / changes (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Deploy Documentation / deploy (push) Has been cancelled
Documentation Site Preview / deploy (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / Release (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Lint Electron Desktop App (push) Has been cancelled
CI / bundle-desktop-unsigned (push) Has been cancelled
lowercase g in goose (#4832)
2025-09-26 23:44:38 -05:00

229 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import requests
import re
import email_validator
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
from python_http_client.exceptions import HTTPError
def main():
# Environment variable validation
required_envs = ["GITHUB_TOKEN", "GITHUB_SHA", "GITHUB_REPOSITORY", "PROVISIONING_API_KEY", "EMAIL_API_KEY"]
missing = [env for env in required_envs if env not in os.environ]
if missing:
print(f"❌ Missing environment variables: {', '.join(missing)}")
exit(2)
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
GITHUB_SHA = os.environ["GITHUB_SHA"]
REPO_NAME = os.environ["GITHUB_REPOSITORY"]
PROVISIONING_API_KEY = os.environ["PROVISIONING_API_KEY"]
SENDGRID_API_KEY = os.environ["EMAIL_API_KEY"]
pr_number = get_pr_number_from_sha()
pr_data = fetch_pr_body(pr_number, GITHUB_TOKEN, REPO_NAME)
pr_body = pr_data.get("body", "")
email = extract_email(pr_body, REPO_NAME, pr_number, GITHUB_TOKEN)
print(f"📬 Found email: {email}")
try:
api_key = provision_api_key(PROVISIONING_API_KEY)
print("✅ API key generated!")
if not send_email(email, api_key, SENDGRID_API_KEY):
print("❌ Email failed to send. Exiting without PR comment.")
exit(2)
comment_on_pr(GITHUB_TOKEN, REPO_NAME, pr_number, email)
except Exception as err:
print(f"❌ An error occurred: {err}")
exit(2)
def get_pr_number_from_sha():
token = os.getenv("GITHUB_TOKEN")
repo = os.getenv("GITHUB_REPOSITORY")
sha = os.getenv("GITHUB_SHA")
url = f"https://api.github.com/repos/{repo}/commits/{sha}/pulls"
headers = {
"Authorization": f"token {token}",
"Accept": "application/vnd.github.groot-preview+json"
}
response = requests.get(url, headers=headers)
response.raise_for_status()
pr_data = response.json()
if pr_data:
return pr_data[0]["number"]
else:
raise Exception("No PR found for this SHA")
def fetch_pr_body(pr_number, github_token, repo_full_name):
print("🔍 Fetching PR body...")
pr_url = f"https://api.github.com/repos/{repo_full_name}/pulls/{pr_number}"
try:
pr_resp = requests.get(
pr_url,
headers={"Authorization": f"Bearer {github_token}"}
)
pr_resp.raise_for_status()
except requests.exceptions.RequestException as e:
print("❌ Failed to fetch PR body:", str(e))
raise
return pr_resp.json()
def extract_email_from_text(text):
email_match = re.search(r"\*\*Email\*\*:\s*([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})", text)
if email_match:
return email_match.group(1)
email_match = re.search(r"[Ee]mail:\s*([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})", text)
if email_match:
return email_match.group(1)
email_match = re.search(r"\b([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})\b", text)
if email_match:
return email_match.group(1)
return None
def fetch_pr_comments(repo_full_name, pr_number, github_token):
comments_url = f"https://api.github.com/repos/{repo_full_name}/issues/{pr_number}/comments"
try:
comments_resp = requests.get(
comments_url,
headers={"Authorization": f"Bearer {github_token}"}
)
comments_resp.raise_for_status()
return comments_resp.json()
except requests.exceptions.RequestException as e:
print(f"⚠️ Failed to fetch PR comments: {e}")
return []
def validate_email_address(email):
try:
valid_email = email_validator.validate_email(email)
normalized_email = valid_email.email
print(f"✅ Email validation passed: {normalized_email}")
return normalized_email
except email_validator.EmailNotValidError as e:
print(f"❌ Email validation failed: {e}")
return None
def extract_email(pr_body, repo_full_name, pr_number, github_token):
print("🔍 Searching for email in PR body...")
email = extract_email_from_text(pr_body)
if email:
print(f"📧 Found email in PR body: {email}")
validated_email = validate_email_address(email)
if validated_email:
return validated_email
else:
print("⚠️ Email in PR body is invalid, checking comments...")
print("🔍 No valid email found in PR body, checking comments...")
comments = fetch_pr_comments(repo_full_name, pr_number, github_token)
for comment in comments:
comment_body = comment.get("body", "")
email = extract_email_from_text(comment_body)
if email:
print(f"📧 Found email in comment by {comment.get('user', {}).get('login', 'unknown')}: {email}")
validated_email = validate_email_address(email)
if validated_email:
return validated_email
else:
print("⚠️ Email in comment is invalid, continuing search...")
print("❌ No valid email found in PR body or comments. Skipping key issuance.")
exit(2)
def provision_api_key(provisioning_api_key):
print("🔐 Creating OpenRouter key...")
try:
key_resp = requests.post(
"https://openrouter.ai/api/v1/keys",
headers={
"Authorization": f"Bearer {provisioning_api_key}",
"Content-Type": "application/json"
},
json={
"name": "goose contributor",
"label": "goose-cookbook",
"limit": 10.0
}
)
key_resp.raise_for_status()
except requests.exceptions.RequestException as e:
print("❌ Failed to provision API key:", str(e))
raise
key = key_resp.json().get("key")
if not key:
print("❌ API response did not include a key.")
exit(2)
return key
def send_email(email, api_key, sendgrid_api_key):
print("📤 Sending email via SendGrid...")
try:
sg = SendGridAPIClient(sendgrid_api_key)
from_email = "goose team <goose@opensource.block.xyz>"
subject = "🎉 Your goose contributor API key"
html_content = f"""
<p>Thank you for contributing to the <strong>goose recipe cookbook</strong>!</p>
<p>🎉 Here's your <strong>$10 OpenRouter API key</strong>:</p>
<pre style="background-color:#f4f4f4;padding:10px;border-radius:6px;"><code>{api_key}</code></pre>
<p>To use this in goose (CLI or Desktop):</p>
<ul>
<li>Go to your <strong>Provider Settings</strong></li>
<li>Select <strong>OpenRouter</strong> from the provider list</li>
<li>Paste your API key</li>
</ul>
<p>📚 Full setup instructions:<br>
<a href="https://block.github.io/goose/docs/getting-started/providers/#configure-provider">
https://block.github.io/goose/docs/getting-started/providers/#configure-provider</a></p>
<p>Happy coding!<br> the goose team</p>
"""
message = Mail(
from_email=from_email,
to_emails=email,
subject=subject,
html_content=html_content
)
response = sg.send(message)
print(f"✅ Email sent successfully! Status code: {response.status_code}")
if response.status_code >= 300:
print(f"⚠️ Warning: Unexpected status code {response.status_code}")
print(f"Response body: {response.body}")
return False
return True
except HTTPError as e:
print(f"❌ SendGrid HTTP error {e.status_code}: {e.body}")
return False
except Exception as e:
print(f"❌ Unexpected error sending email: {type(e).__name__}: {e}")
return False
def comment_on_pr(github_token, repo_full_name, pr_number, email):
print("💬 Commenting on PR...")
comment_url = f"https://api.github.com/repos/{repo_full_name}/issues/{pr_number}/comments"
try:
comment_resp = requests.post(
comment_url,
headers={
"Authorization": f"Bearer {github_token}",
"Accept": "application/vnd.github+json"
},
json={
"body": f"✅ $10 OpenRouter API key sent to `{email}`. Thanks for your contribution to the goose cookbook!"
}
)
comment_resp.raise_for_status()
print("✅ Confirmation comment added to PR.")
except requests.exceptions.RequestException as e:
print("❌ Failed to comment on PR:", str(e))
raise
if __name__ == "__main__":
main()