Skip to content

Commit a23ced7

Browse files
pokuttaclaude
andcommitted
Add daily blog feed + org stats GitHub Action
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5eacfa9 commit a23ced7

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

.github/workflows/fetch-blog.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: Fetch blog feed and org stats
2+
3+
on:
4+
schedule:
5+
- cron: '17 6 * * *' # daily at 06:17 UTC
6+
push:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
fetch:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Fetch and convert Atom feed to JSON
20+
run: |
21+
python3 - <<'PYEOF'
22+
import urllib.request, xml.etree.ElementTree as ET, json, re, html
23+
24+
FEED = 'https://www.pokutta.com/blog/feed.xml'
25+
MAX = 5
26+
NS = {'a': 'http://www.w3.org/2005/Atom'}
27+
28+
data = urllib.request.urlopen(FEED).read()
29+
root = ET.fromstring(data)
30+
posts = []
31+
for entry in root.findall('a:entry', NS)[:MAX]:
32+
title = entry.findtext('a:title', '', NS)
33+
title = html.unescape(re.sub(r'<[^>]*>', '', title))
34+
link = ''
35+
for l in entry.findall('a:link', NS):
36+
if l.get('rel', 'alternate') == 'alternate':
37+
link = l.get('href', '')
38+
break
39+
summary = entry.findtext('a:summary', '', NS)
40+
summary = html.unescape(re.sub(r'<[^>]*>', '', summary)).strip()
41+
if len(summary) > 200:
42+
summary = summary[:200].rsplit(' ', 1)[0] + '…'
43+
date = entry.findtext('a:published', '', NS)[:10] # YYYY-MM-DD
44+
posts.append({'title': title, 'link': link, 'summary': summary, 'date': date})
45+
46+
with open('blog-feed.json', 'w') as f:
47+
json.dump(posts, f, indent=2)
48+
print(f'Wrote {len(posts)} posts to blog-feed.json')
49+
PYEOF
50+
51+
- name: Fetch GitHub org stats
52+
env:
53+
GH_TOKEN: ${{ github.token }}
54+
run: |
55+
python3 - <<'PYEOF'
56+
import urllib.request, json, os
57+
58+
TOKEN = os.environ.get('GH_TOKEN', '')
59+
ORG = 'ZIB-IOL'
60+
61+
def gh_get(path):
62+
url = f'https://api.github.com/{path}'
63+
req = urllib.request.Request(url)
64+
if TOKEN:
65+
req.add_header('Authorization', f'Bearer {TOKEN}')
66+
req.add_header('Accept', 'application/vnd.github+json')
67+
return json.loads(urllib.request.urlopen(req).read())
68+
69+
# Paginate members
70+
members = 0
71+
page = 1
72+
while True:
73+
data = gh_get(f'orgs/{ORG}/members?per_page=100&page={page}')
74+
members += len(data)
75+
if len(data) < 100:
76+
break
77+
page += 1
78+
79+
# Paginate repos
80+
repos = 0
81+
page = 1
82+
while True:
83+
data = gh_get(f'orgs/{ORG}/repos?per_page=100&page={page}')
84+
repos += len(data)
85+
if len(data) < 100:
86+
break
87+
page += 1
88+
89+
stats = {'members': members, 'repos': repos}
90+
with open('org-stats.json', 'w') as f:
91+
json.dump(stats, f, indent=2)
92+
print(f'Org stats: {stats}')
93+
PYEOF
94+
95+
- name: Commit if changed
96+
run: |
97+
git config user.name "github-actions[bot]"
98+
git config user.email "github-actions[bot]@users.noreply.github.com"
99+
git add blog-feed.json org-stats.json
100+
git diff --cached --quiet || git commit -m "Update blog feed and org stats" && git push

0 commit comments

Comments
 (0)