Skip to content

Commit 2a8d9f9

Browse files
authored
Merge pull request #37 from datum-cloud/fwidjaja-patch-3
Add automation for weekly alt-clouds monitoring
2 parents 1a8d432 + b14f75d commit 2a8d9f9

File tree

7 files changed

+1198
-0
lines changed

7 files changed

+1198
-0
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Handle Issue Approvals
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
jobs:
8+
handle-approval:
9+
if: |
10+
contains(github.event.issue.labels.*.name, 'automation') &&
11+
(contains(github.event.comment.body, 'approve') || contains(github.event.comment.body, 'reject'))
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: write
15+
issues: write
16+
pull-requests: write
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: '3.11'
26+
27+
- name: Install dependencies
28+
run: |
29+
pip install requests beautifulsoup4
30+
31+
- name: Process approval/rejection
32+
env:
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34+
ISSUE_NUMBER: ${{ github.event.issue.number }}
35+
COMMENT_BODY: ${{ github.event.comment.body }}
36+
ISSUE_BODY: ${{ github.event.issue.body }}
37+
ISSUE_TITLE: ${{ github.event.issue.title }}
38+
run: |
39+
python scripts/handle_issue_comment.py
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Weekly Alt Clouds Monitor
2+
3+
on:
4+
schedule:
5+
# Every Monday at 10 AM UTC
6+
- cron: '0 10 * * 1'
7+
workflow_dispatch: # Manual trigger for testing
8+
9+
jobs:
10+
monitor-and-evaluate:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: write
14+
issues: write
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: '3.11'
24+
25+
- name: Install dependencies
26+
run: |
27+
pip install anthropic requests beautifulsoup4 lxml
28+
29+
- name: Run news monitoring
30+
env:
31+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
32+
run: |
33+
python scripts/monitor_news.py
34+
35+
- name: Evaluate candidates
36+
env:
37+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
38+
run: |
39+
python scripts/evaluate_candidates.py
40+
41+
- name: Create GitHub issues for candidates
42+
env:
43+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
run: |
45+
python scripts/create_issues.py
46+
47+
- name: Commit scan results
48+
run: |
49+
git config user.name "github-actions[bot]"
50+
git config user.email "github-actions[bot]@users.noreply.github.com"
51+
git add data/
52+
git diff --staged --quiet || git commit -m "Weekly scan: $(date +%Y-%m-%d)"
53+
git push
54+
55+
- name: Send Slack notification
56+
env:
57+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
58+
run: |
59+
python scripts/slack_notifier.py

scripts/create_issues.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Create GitHub issues for evaluated candidates.
4+
ALWAYS creates issues for score 2/3 and 3/3 (no auto-PR).
5+
"""
6+
7+
import json
8+
import os
9+
import sys
10+
from datetime import datetime
11+
from pathlib import Path
12+
import requests
13+
14+
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
15+
REPO_OWNER = 'datum-cloud'
16+
REPO_NAME = 'awesome-alt-clouds'
17+
18+
# GitHub API setup
19+
GITHUB_API = 'https://api.github.com'
20+
headers = {
21+
'Authorization': f'token {GITHUB_TOKEN}',
22+
'Accept': 'application/vnd.github.v3+json'
23+
}
24+
25+
26+
def create_issue(candidate):
27+
"""Create a GitHub issue for a candidate cloud service."""
28+
29+
score = candidate['evaluation']['score']
30+
name = candidate['name']
31+
url = candidate['url']
32+
category = candidate['category']
33+
description = candidate['evaluation']['description']
34+
35+
# Determine label based on score
36+
if score == 3:
37+
label = 'ready-to-merge'
38+
emoji = '✅'
39+
status = 'Ready to Merge'
40+
elif score == 2:
41+
label = 'needs-review'
42+
emoji = '⚠️'
43+
status = 'Needs Review'
44+
else:
45+
# Don't create issues for score 0-1
46+
return None
47+
48+
# Build issue title
49+
title = f"{emoji} Add {name} to {category}"
50+
51+
# Build issue body
52+
body = f"""## {name}
53+
54+
**Status**: {status}
55+
**Score**: {score}/3
56+
**Category**: {category}
57+
**URL**: {url}
58+
59+
### Proposed Description
60+
{description}
61+
62+
### Evaluation Results
63+
64+
#### ✓ Criteria Met ({score}/3)
65+
66+
"""
67+
68+
# Add criteria details
69+
for criterion in candidate['evaluation']['criteria']:
70+
check = "✅" if criterion['passed'] else "❌"
71+
body += f"{check} **{criterion['name']}**\n"
72+
body += f"- Evidence: {criterion['evidence']}\n"
73+
if criterion['url']:
74+
body += f"- Source: {criterion['url']}\n"
75+
body += "\n"
76+
77+
# Add instructions
78+
if score == 3:
79+
body += """
80+
### Next Steps
81+
This candidate meets all 3 criteria and is ready to be added!
82+
83+
**To approve**: Comment `approve` and I'll create a PR to add this service.
84+
**To reject**: Comment `reject` with a reason.
85+
"""
86+
else:
87+
body += """
88+
### Next Steps
89+
This candidate meets 2/3 criteria and needs manual review.
90+
91+
**To approve**: Verify the missing criterion manually, then comment `approve` to create a PR.
92+
**To reject**: Comment `reject` with a reason.
93+
"""
94+
95+
# Create the issue
96+
issue_data = {
97+
'title': title,
98+
'body': body,
99+
'labels': [label, 'automation']
100+
}
101+
102+
response = requests.post(
103+
f'{GITHUB_API}/repos/{REPO_OWNER}/{REPO_NAME}/issues',
104+
headers=headers,
105+
json=issue_data
106+
)
107+
108+
if response.status_code == 201:
109+
issue = response.json()
110+
print(f"✅ Created issue #{issue['number']}: {name}")
111+
return issue['number']
112+
else:
113+
print(f"❌ Failed to create issue for {name}: {response.text}")
114+
return None
115+
116+
117+
def main():
118+
# Find latest evaluation file
119+
eval_dir = Path('data/evaluations')
120+
if not eval_dir.exists():
121+
print("No evaluations directory found")
122+
return
123+
124+
eval_files = sorted(eval_dir.glob('eval-*.json'))
125+
if not eval_files:
126+
print("No evaluation files found")
127+
return
128+
129+
latest_eval = eval_files[-1]
130+
print(f"Processing: {latest_eval}")
131+
132+
with open(latest_eval) as f:
133+
data = json.load(f)
134+
135+
candidates = data.get('candidates', [])
136+
137+
# Filter for score 2 or 3
138+
eligible = [c for c in candidates if c['evaluation']['score'] >= 2]
139+
140+
if not eligible:
141+
print("No candidates with score 2+ found")
142+
return
143+
144+
print(f"\nFound {len(eligible)} candidates to create issues for")
145+
146+
issues_created = 0
147+
for candidate in eligible:
148+
issue_num = create_issue(candidate)
149+
if issue_num:
150+
issues_created += 1
151+
152+
print(f"\n✅ Created {issues_created} issues")
153+
154+
# Save summary
155+
summary = {
156+
'date': datetime.now().isoformat(),
157+
'total_evaluated': len(candidates),
158+
'issues_created': issues_created,
159+
'ready_to_merge': len([c for c in eligible if c['evaluation']['score'] == 3]),
160+
'needs_review': len([c for c in eligible if c['evaluation']['score'] == 2])
161+
}
162+
163+
summary_file = eval_dir / f"summary-{datetime.now().strftime('%Y%m%d')}.json"
164+
with open(summary_file, 'w') as f:
165+
json.dump(summary, f, indent=2)
166+
167+
print(f"Summary saved to: {summary_file}")
168+
169+
170+
if __name__ == '__main__':
171+
if not GITHUB_TOKEN:
172+
print("Error: GITHUB_TOKEN not found")
173+
sys.exit(1)
174+
main()

0 commit comments

Comments
 (0)