-
Notifications
You must be signed in to change notification settings - Fork 0
376 lines (332 loc) · 14.8 KB
/
github-metrics-notify.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# .github/workflows/github-metrics-notify.yml
name: GitHub Metrics Notification
# Grants specific permissions to the GITHUB_TOKEN
permissions:
contents: write # Allows pushing changes to the repository
issues: read # Optional: if you're interacting with issues
pull-requests: write # Optional: if you're interacting with pull requests
# Triggers the workflow on specific GitHub events and schedules it as a backup
on:
# Trigger on repository star events
watch:
types: [started, deleted] # 'started' for star, 'deleted' for unstar
# Trigger on repository forks
fork:
# Trigger on issue events
issues:
types: [opened, closed, reopened, edited]
# Trigger on pull request events
pull_request:
types: [opened, closed, reopened, edited]
# Trigger on release events
release:
types: [published, edited, prereleased, released]
# Trigger on push events to the main branch
push:
branches:
- main
# Scheduled backup trigger every hour for followers/subscribers
schedule:
- cron: '0 */1 * * *' # Every hour at minute 0
# Allows manual triggering
workflow_dispatch:
jobs:
notify_metrics:
runs-on: ubuntu-latest
steps:
# Step 1: Checkout the repository
- name: Checkout Repository
uses: actions/checkout@v3
with:
persist-credentials: true # Enables Git commands to use GITHUB_TOKEN
fetch-depth: 0 # Fetch all history for accurate metric tracking
# Step 2: Set Up Python Environment
- name: Set Up Python
uses: actions/setup-python@v4
with:
python-version: '3.x' # Specify the Python version
# Step 3: Install Python Dependencies
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install requests
# Step 4: Fetch and Compare Metrics
- name: Fetch and Compare Metrics
id: fetch_metrics
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Built-in secret provided by GitHub Actions
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} # Your Discord webhook URL
GITHUB_EVENT_NAME: ${{ github.event_name }} # To determine if run is manual or triggered by an event
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} # Dynamic repository owner
run: |
python3 - <<'EOF' > fetch_metrics.out
import os
import requests
import json
from datetime import datetime
# Configuration
REPO_OWNER = os.getenv('GITHUB_REPOSITORY_OWNER')
REPO_NAME = os.getenv('GITHUB_REPOSITORY').split('/')[-1]
METRICS_FILE = ".github/metrics.json"
# Ensure .github directory exists
os.makedirs(os.path.dirname(METRICS_FILE), exist_ok=True)
# GitHub API Headers
headers = {
"Authorization": f"token {os.getenv('GITHUB_TOKEN')}",
"Accept": "application/vnd.github.v3+json"
}
# Function to fetch closed issues count using GitHub Search API
def fetch_closed_issues(owner, repo, headers):
search_api = f"https://api.github.com/search/issues?q=repo:{owner}/{repo}+type:issue+state:closed"
try:
response = requests.get(search_api, headers=headers)
response.raise_for_status()
data = response.json()
return data.get('total_count', 0)
except requests.exceptions.RequestException as e:
print(f"Error fetching closed issues count: {e}")
return 0
# Fetch current metrics from GitHub API
repo_api = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}"
try:
response = requests.get(repo_api, headers=headers)
response.raise_for_status()
repo_data = response.json()
stars = repo_data.get('stargazers_count', 0)
forks = repo_data.get('forks_count', 0)
followers = repo_data.get('subscribers_count', 0)
open_issues = repo_data.get('open_issues_count', 0)
closed_issues = fetch_closed_issues(REPO_OWNER, REPO_NAME, headers)
except requests.exceptions.RequestException as e:
print(f"Error fetching repository data: {e}")
exit(1)
# Function to load previous metrics with error handling
def load_previous_metrics(file_path):
try:
with open(file_path, 'r') as file:
return json.load(file)
except json.decoder.JSONDecodeError:
print("metrics.json is corrupted or empty. Reinitializing.")
return {
"stars": 0,
"forks": 0,
"followers": 0,
"open_issues": 0,
"closed_issues": 0
}
except FileNotFoundError:
return {
"stars": 0,
"forks": 0,
"followers": 0,
"open_issues": 0,
"closed_issues": 0
}
# Load previous metrics
prev_metrics = load_previous_metrics(METRICS_FILE)
is_initial_run = not os.path.exists(METRICS_FILE)
# Determine changes (both increases and decreases)
changes = {}
metrics = ["stars", "forks", "followers", "open_issues", "closed_issues"]
current_metrics = {
"stars": stars,
"forks": forks,
"followers": followers,
"open_issues": open_issues,
"closed_issues": closed_issues
}
for metric in metrics:
current = current_metrics.get(metric, 0)
previous = prev_metrics.get(metric, 0)
if current != previous:
changes[metric] = current - previous
# Update metrics file
with open(METRICS_FILE, 'w') as file:
json.dump(current_metrics, file)
# Determine if a notification should be sent
event_name = os.getenv('GITHUB_EVENT_NAME')
send_notification = False
no_changes = False
initial_setup = False
if is_initial_run:
if event_name == 'workflow_dispatch':
# Manual run: Send notification for initial setup
send_notification = True
initial_setup = True
elif event_name == 'watch' and changes.get('stars'):
# Initial run triggered by a star event: Send notification
send_notification = True
else:
# Event-triggered runs: Do not send notification on initial setup
send_notification = False
else:
if event_name == 'workflow_dispatch':
# Manual run: Always send notification
send_notification = True
if not changes:
no_changes = True
elif event_name == 'watch':
# Star event: Send notification only if stars changed
if changes.get('stars'):
send_notification = True
else:
# Scheduled run or other events: Send notification only if there are changes
if changes:
send_notification = True
if send_notification:
triggering_actor = os.getenv('GITHUB_ACTOR', 'Unknown')
# Prepare Discord notification payload
payload = {
"embeds": [
{
"title": "📈 GitHub Repository Metrics Updated",
"url": f"https://github.com/{REPO_OWNER}/{REPO_NAME}", # Link back to the repository
"color": 0x7289DA, # Discord blurple color
"thumbnail": {
"url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" # GitHub logo
},
"fields": [
{
"name": "📂 Repository",
"value": f"[{REPO_OWNER}/{REPO_NAME}](https://github.com/{REPO_OWNER}/{REPO_NAME})",
"inline": False
},
{
"name": "⭐ Stars",
"value": f"{stars}",
"inline": True
},
{
"name": "🍴 Forks",
"value": f"{forks}",
"inline": True
},
{
"name": "👥 Followers",
"value": f"{followers}",
"inline": True
},
{
"name": "🐛 Open Issues",
"value": f"{open_issues}",
"inline": True
},
{
"name": "🔒 Closed Issues",
"value": f"{closed_issues}",
"inline": True
},
{
"name": "👤 Triggered By",
"value": triggering_actor,
"inline": False
},
],
"footer": {
"text": "GitHub Metrics Monitor",
"icon_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" # GitHub logo
},
"timestamp": datetime.utcnow().isoformat() # Adds a timestamp to the embed
}
]
}
if initial_setup:
# Add a field indicating initial setup
payload["embeds"][0]["fields"].append({
"name": "⚙️ Initial Setup",
"value": "Metrics tracking has been initialized.",
"inline": False
})
elif changes:
# Add fields for each updated metric
for metric, count in changes.items():
emoji = {
"stars": "⭐",
"forks": "🍴",
"followers": "👥",
"open_issues": "🐛",
"closed_issues": "🔒"
}.get(metric, "")
change_symbol = "+" if count > 0 else ""
payload["embeds"][0]["fields"].append({
"name": f"{emoji} {metric.replace('_', ' ').capitalize()} (Change)",
"value": f"{change_symbol}{count}",
"inline": True
})
elif no_changes:
# Indicate that there were no changes during a manual run
payload["embeds"][0]["fields"].append({
"name": "✅ No Changes",
"value": "No updates to metrics since the last check.",
"inline": False
})
# Save payload to a temporary file for the next step
with open('payload.json', 'w') as f:
json.dump(payload, f)
# Output whether to send notification
if initial_setup or changes or no_changes:
print("SEND_NOTIFICATION=true")
else:
print("SEND_NOTIFICATION=false")
else:
print("SEND_NOTIFICATION=false")
EOF
# Step 5: Ensure .gitignore Ignores Temporary Files
- name: Ensure .gitignore Ignores Temporary Files
run: |
# Check if .gitignore exists; if not, create it
if [ ! -f .gitignore ]; then
touch .gitignore
fi
# Add 'fetch_metrics.out' if not present
if ! grep -Fxq "fetch_metrics.out" .gitignore; then
echo "fetch_metrics.out" >> .gitignore
echo "Added 'fetch_metrics.out' to .gitignore"
else
echo "'fetch_metrics.out' already present in .gitignore"
fi
# Add 'payload.json' if not present
if ! grep -Fxq "payload.json" .gitignore; then
echo "payload.json" >> .gitignore
echo "Added 'payload.json' to .gitignore"
else
echo "'payload.json' already present in .gitignore"
fi
# Step 6: Decide Whether to Send Notification
- name: Check if Notification Should Be Sent
id: decide_notification
run: |
if grep -q "SEND_NOTIFICATION=true" fetch_metrics.out; then
echo "send=true" >> $GITHUB_OUTPUT
else
echo "send=false" >> $GITHUB_OUTPUT
fi
shell: bash
# Step 7: Send Discord Notification using curl
- name: Send Discord Notification
if: steps.decide_notification.outputs.send == 'true'
run: |
curl -H "Content-Type: application/json" -d @payload.json ${{ secrets.DISCORD_WEBHOOK_URL }}
# Step 8: Commit and Push Updated metrics.json and .gitignore
- name: Commit and Push Changes
if: steps.decide_notification.outputs.send == 'true'
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "[email protected]"
# Stage metrics.json
git add .github/metrics.json
# Stage .gitignore only if it was modified
if git diff --name-only | grep -q "^\.gitignore$"; then
git add .gitignore
else
echo ".gitignore not modified"
fi
# Commit changes if there are any
git commit -m "Update metrics.json and ensure temporary files are ignored [skip ci]" || echo "No changes to commit"
# Push changes to the main branch
git push origin main # Replace 'main' with your default branch if different
# Step 9: Clean Up Temporary Files
- name: Clean Up Temporary Files
if: always()
run: |
rm -f fetch_metrics.out payload.json