Skip to content

Commit 7c1aa11

Browse files
Merge branch 'main' into main
2 parents 96dc1c8 + 371b756 commit 7c1aa11

File tree

2 files changed

+62
-39
lines changed

2 files changed

+62
-39
lines changed

.github/workflows/copilot.yml

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ jobs:
3535
- name: Run AI Code Reviewer
3636
env:
3737
GITHUB_TOKEN: ${{ steps.generated_token.outputs.token }}
38+
BOT: ${{ secrets.BOT }}
3839
GITHUB_REPOSITORY: ${{ github.repository }}
3940
PR_NUMBER: ${{ github.event.pull_request.number }}
4041
run: python etc/tool/copilot.py

etc/tool/copilot.py

+61-39
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
sys.path.append(str(Path(__file__).parent.parent.parent))
1010

1111
# GitHub API credentials
12-
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
12+
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") # Used for commenting and requesting changes
13+
BOT_GITHUB_TOKEN = os.getenv("BOT") # Used for approvals
1314
GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY")
1415
PR_NUMBER = os.getenv("PR_NUMBER")
1516

@@ -18,13 +19,14 @@
1819

1920
# PR Review Rules
2021
README_RULES = """
21-
1. Subdomains must be for personal sites, open-source projects, or legitimate services.
22-
2. JSON must contain 'domain', 'subdomain', 'owner', and 'records'.
23-
3. Wildcard domains require justification.
24-
4. Cloudflare (NS), Netlify, and Vercel are not supported.
25-
5. Illegal or inappropriate domain use is prohibited.
26-
6. PR descriptions must be clear.
27-
7. Only new domains (is-epic.me and is-awsm.tech) are allowed.
22+
### PR Review Guidelines
23+
1. **Subdomains must be valid**: Only for personal sites, open-source projects, or legitimate services.
24+
2. **JSON Structure**: Each file must contain `domain`, `subdomain`, `owner`, and `records`.
25+
3. **No Wildcard Abuse**: Wildcard domains (`*.example.com`) require proper justification.
26+
4. **Disallowed DNS Providers**: Cloudflare (NS), Netlify, and Vercel are **not allowed**.
27+
5. **Legal & Appropriate Usage**: Domains must not be used for illegal or inappropriate purposes.
28+
6. **Clear Descriptions**: PR descriptions should explain why the domain is needed.
29+
7. **Domain Restrictions**: **Only `is-epic.me` and `is-awsm.tech`** are allowed.
2830
"""
2931

3032
def fetch_changed_files(pr):
@@ -42,50 +44,77 @@ def fetch_file_content(repo, filename):
4244
def ai_review_pr(pr_body, changed_files, file_contents):
4345
"""Uses AI to review the PR based on the rules."""
4446
review_prompt = f"""
45-
Review the PR based on these rules:
47+
**Task:** Review this Pull Request based on the following rules:
4648
4749
{README_RULES}
4850
49-
PR Description: {pr_body}
50-
Changed Files: {', '.join(changed_files)}
51+
**PR Description:**
52+
{pr_body}
5153
52-
File Contents:
54+
**Changed Files:**
55+
{', '.join(changed_files)}
56+
57+
**File Contents:**
5358
{file_contents}
5459
55-
- Approve if all rules are met.
56-
- Reject if old domains (`is-cool.me`, `is-app.tech`) are used.
57-
- If the JSON structure is missing or incorrect, request changes.
60+
---
61+
62+
**Expected Output Format:**
63+
- If the PR is correct, respond with:
64+
✅ PR Approved. No issues found.
65+
- If issues exist, respond with:
66+
- **Structured comments per issue.**
67+
- **GitHub Actions-style comments**, e.g.:
68+
- 'Consider handling session failures...'
69+
- 'Avoid using a generic exception handler...'
70+
- 'Ensure that the import statement for `BlackboxAPI` aligns with others...'
71+
72+
**DO NOT** just say "Request changes"—explain why!
5873
"""
5974

6075
try:
6176
response = g4f.ChatCompletion.create(
6277
model=g4f.models.gpt_4,
6378
messages=[{"role": "user", "content": review_prompt}]
6479
)
65-
66-
# Ensure response is a string
67-
decision = response.get("content", "").strip().lower() if isinstance(response, dict) else response.strip().lower()
6880

69-
# Log unexpected responses
81+
decision = response.get("content", "").strip() if isinstance(response, dict) else response.strip()
82+
83+
# If AI fails or response is empty, request changes automatically
7084
if not decision:
7185
print("❌ AI response is empty or invalid. Defaulting to 'request changes'.")
72-
return "request changes"
86+
return "request changes", ["AI review failed. Please manually check."]
7387

74-
# Force rejection if old domains are detected
75-
if "is-cool.me" in file_contents or "is-app.tech" in file_contents:
76-
print("❌ PR rejected due to use of old domains.")
77-
return "reject"
88+
# If AI finds issues, extract structured comments
89+
if "consider" in decision.lower() or "avoid" in decision.lower():
90+
return "request changes", decision.split("\n")
7891

79-
return decision
92+
return "approve", []
8093

8194
except Exception as e:
8295
print(f"❌ AI review failed: {e}")
83-
return "request changes"
96+
return "request changes", ["AI review failed. Please manually check."]
8497

8598
def post_comment(pr, message):
8699
"""Posts a comment on the PR."""
87100
pr.create_issue_comment(message)
88101

102+
def approve_pr(repo, pr):
103+
"""Approves the PR using the bot's personal GitHub token."""
104+
bot_github = Github(BOT_GITHUB_TOKEN)
105+
bot_repo = bot_github.get_repo(GITHUB_REPOSITORY)
106+
bot_pr = bot_repo.get_pull(int(PR_NUMBER))
107+
108+
bot_pr.create_review(event="APPROVE", body="✅ AI Code Reviewer (Bot) has approved this PR.")
109+
print("✅ PR Approved by AI (Using Bot Token)")
110+
111+
def request_changes(repo, pr, comments):
112+
"""Requests changes on the PR using the default GitHub Actions token."""
113+
formatted_comments = "\n\n".join([f"⚠️ **{comment}**" for comment in comments])
114+
pr.create_review(event="REQUEST_CHANGES", body=f"⚠️ AI Review suggests changes:\n\n{formatted_comments}")
115+
post_comment(pr, f"⚠️ AI Review:\n\n{formatted_comments}")
116+
print("⚠️ PR Needs Changes")
117+
89118
def main():
90119
github = Github(GITHUB_TOKEN)
91120
repo = github.get_repo(GITHUB_REPOSITORY)
@@ -94,19 +123,12 @@ def main():
94123
changed_files = fetch_changed_files(pr)
95124
file_contents = "\n\n".join([f"### {file}\n{fetch_file_content(repo, file)}" for file in changed_files])
96125

97-
decision = ai_review_pr(pr.body, changed_files, file_contents)
98-
99-
if "approve" in decision:
100-
pr.create_review(event="APPROVE", body="✅ AI Code Reviewer has approved this PR.")
101-
print("✅ PR Approved by AI")
102-
elif "reject" in decision:
103-
pr.create_review(event="REQUEST_CHANGES", body="❌ PR rejected due to rule violations.")
104-
post_comment(pr, "❌ Your PR violates the domain rules (e.g., using `is-cool.me`). Please update and resubmit.")
105-
print("❌ PR Rejected")
106-
else:
107-
pr.create_review(event="REQUEST_CHANGES", body="⚠️ AI Code Reviewer suggests changes.")
108-
post_comment(pr, "⚠️ AI Reviewer suggests changes:\n\n" + decision)
109-
print("⚠️ PR Needs Changes")
126+
decision, comments = ai_review_pr(pr.body, changed_files, file_contents)
127+
128+
if decision == "approve":
129+
approve_pr(repo, pr) # Uses bot token for approval
130+
elif decision == "request changes":
131+
request_changes(repo, pr, comments) # Uses GitHub Actions token for requests
110132

111133
if __name__ == "__main__":
112134
main()

0 commit comments

Comments
 (0)