GitHub profilinize giren biri sizi tanımak ister. Kim olduğunuzu, neler yaptığınızı, neler yazdığınızı görmek ister. Ama çoğu profil README'si bir kere yazılır, sonra yıllarca öylece durur. 2021'den kalma "latest posts" listesi, süresi geçmiş projeler, hâlâ güncellenmeyi bekleyen teknoloji stack'i.
Peki ya README'niz her sabah kendi kendini güncelleseydi?
Bu yazıda Medium ve dev.to makalelerinizi otomatik olarak çeken, GitHub Actions ile her gün çalışan ve README'nizi canlı tutan bir sistem kuracağız. Python scriptten workflow dosyasına, placeholder marker'lardan manuel tetiklemeye kadar her adımı tek tek inceleyeceğiz.
1. Nasıl Çalışır? 🔄

Kuracağımız sistem dört parçadan oluşuyor:
README.md içine özel HTML yorum satırları (<!-- MEDIUM-ARTICLES:START --> gibi) yerleştiriyoruz. Script bu marker'lar arasındaki içeriği bulup değiştiriyor; marker'ların dışındaki hiçbir şeye dokunmuyor.
Python scripti Medium'un RSS feed'ini ve dev.to'nun REST API'sini çağırıyor, makale başlıklarını ve URL'lerini topluyor, ardından README'yi güncelliyor.
GitHub Actions workflow'u bu scripti zamanlanmış olarak çalıştırıyor. Her gün sabah 06:00 UTC'de tetikleniyor; script bir değişiklik yapıyorsa otomatik olarak commit atıp push ediyor.
Manuel tetikleme seçeneği sayesinde beklemek zorunda kalmıyorsunuz. Yeni bir makale yayınladığınız an Actions sekmesinden tek tıkla çalıştırabiliyorsunuz.
💡 Sistem tamamen GitHub'ın kendi altyapısında çalışır. Harici bir sunucuya, ücretli bir servise veya kendi makinenizin açık olmasına ihtiyaç yoktur. GitHub Actions, public repolar için ücretsizdir.
2. GitHub Profil README'si Nedir? 👤

GitHub, kullanıcı adınızla aynı isimde bir repo oluşturduğunuzda içindeki README.md dosyasını otomatik olarak profil sayfanızda gösterir. Bu özel bir GitHub özelliğidir.
Github Profil README’si Nasıl Oluşturulur?
- GitHub'da New repository butonuna tıklayın
- Repository name olarak kendi kullanıcı adınızı yazın (örneğin yasinatesim)
- Public seçili olsun
- Add a README file kutucuğunu işaretleyin
- Create repository butonuna tıklayın
Repo oluşturduğunuzda GitHub sizi "✨ yasinatesim/yasinatesim is a special repository" mesajıyla karşılar. README.md dosyasına yazdığınız her şey profilinizde görünmeye başlar.
⚠️ Repository adı Github kullanıcı adınızla birebir aynı olmalıdır, büyük/küçük harf dahil.
3. Github Profil README'ye Placeholder Marker Eklemek 📍

Script README'de neyi güncelleyeceğini nasıl biliyor? HTML yorum satırları ile koyduğumuz özel marker'lar sayesinde. Bu marker'lar tarayıcıda veya GitHub'da görünmez, ama script için birer koordinat görevi görür.
README'nizin makaleleri göstermesini istediğiniz yerine şu bloğu ekleyin:
1## ✍️ Latest Medium Articles
2
3<!-- MEDIUM-ARTICLES:START -->
4<!-- MEDIUM-ARTICLES:END -->
5
6## 📝 Latest dev.to Articles
7
8<!-- DEVTO-ARTICLES:START -->
9<!-- DEVTO-ARTICLES:END -->Script çalıştığında bu iki marker arasındaki her şeyi siler ve yerine güncel makale listesini yazar. Marker'ların dışındaki hiçbir satıra dokunmaz. İlk çalıştırmadan sonra görünüm şöyle olacak;
1## ✍️ Latest Medium Articles
2
3<!-- MEDIUM-ARTICLES:START -->
4- [Micro Frontend Mimarisi (React Örnekleriyle)](https://medium.com/...)
5- [Google Lighthouse'u Yazılım Geliştirme Sürecimize Nasıl Entegre Ettik?](https://medium.com/...) *(Hepsiburadatech)*
6- [A'dan Z'ye Typescript](https://medium.com/...)
7<!-- MEDIUM-ARTICLES:END -->💡 Yukarıdaki örnek kendi Github Profil README dosyama ait.
Publication bilgisi (parantez içindeki kanal adı) script tarafından makale URL'sinden otomatik çıkarılır. medium.com/publication/... gibi bir URL'yi görünce slug'ı alıp parantez içinde makale başlığının yanına ekler (Yukarıdaki çıktıdaki 2. Satırda bunu gösterdim).
Kişisel profil yazıları için (medium.com/@yasinatesim/...) parantez göstermez.
4. Python Script: Makale Verilerini Çekmek 🐍

Scriptin tamamı iki harici kütüphane kullanıyor: feedparser (Medium RSS için) ve requests (dev.to API için). Başka hiçbir bağımlılık yok.
1scripts/
2└── fetch_articles.py4.1 Temel Yapı
1"""
2fetch_articles.py
3-----------------
4Medium RSS ve dev.to API'sinden tüm makaleleri çeker,
5README.md içindeki placeholder'ları otomatik günceller.
6"""
7
8import re
9import feedparser
10import requests
11
12MEDIUM_USERNAME = "kullanici-adiniz" # @ işareti olmadan
13DEVTO_USERNAME = "kullanici-adiniz"
14README_PATH = "README.md"
15
16MEDIUM_START = "<!-- MEDIUM-ARTICLES:START -->"
17MEDIUM_END = "<!-- MEDIUM-ARTICLES:END -->"
18DEVTO_START = "<!-- DEVTO-ARTICLES:START -->"
19DEVTO_END = "<!-- DEVTO-ARTICLES:END -->"4.2 Medium Makalelerini Çekmek
Medium her kullanıcı için herkese açık bir RSS feed sunar. feedparser bu XML feed'i Python objelerine dönüştürür. Limit koymuyoruz; feed.entries listesinin tamamını işliyoruz.
1def fetch_medium_articles():
2 url = f"https://medium.com/feed/@{MEDIUM_USERNAME}"
3 feed = feedparser.parse(url)
4 articles = []
5
6 for entry in feed.entries:
7 publication = None
8 link = entry.link
9
10 # URL'den publication bilgisini çıkar
11 # medium.com/publication-slug/... → publication var
12 # medium.com/@username/... → kişisel profil, publication yok
13 subdomain_match = re.match(r"https://([^.]+)\.medium\.com/", link)
14 path_match = re.match(r"https://medium\.com/([^@/][^/]*)/", link)
15
16 if subdomain_match:
17 slug = subdomain_match.group(1)
18 publication = slug.replace("-", " ").title()
19 elif path_match:
20 slug = path_match.group(1)
21 # Sistem path'lerini filtrele (tag, search, topic vb.)
22 system_paths = {"tag", "tags", "search", "topic", "topics",
23 "m", "about", "membership"}
24 if slug not in system_paths:
25 publication = slug.replace("-", " ").title()
26
27 articles.append({
28 "title": entry.title,
29 "url": link,
30 "publication": publication,
31 })
32
33 return articles⚠️ Medium RSS feed'i varsayılan olarak son 10 makaleyi döndürür. Bu Medium'un koyduğu bir kısıtlamadır; scriptiniz ne kadar uğraşırsa uğraşsın 10'dan fazlasını RSS üzerinden çekemez. Tüm makalelerinizi göstermek istiyorsanız scraping yöntemlerini araştırmanız gerekir.
Şuan benim Medium’da yayınlanmış 10 tane makalen olmadığı için henüz kendi Github Profil README dosyama entegrasyon sağlamadım. 😁 10 makaleyi geçtikten sonra Github reposunu güncellerim. Repo linki makale sonundaki “Demo” kısmında
4.3 dev.to Makalelerini Çekmek
dev.to'nun açık bir REST API'si var ve API anahtarı gerektirmiyor. Sayfalama desteği olduğundan while döngüsüyle tüm makaleleri çekebiliyoruz:
1def fetch_devto_articles():
2 page, articles = 1, []
3
4 while True:
5 url = (
6 f"https://dev.to/api/articles"
7 f"?username={DEVTO_USERNAME}&per_page=100&page={page}"
8 )
9 response = requests.get(url, timeout=10)
10
11 if response.status_code != 200:
12 break
13
14 batch = response.json()
15 if not batch: # Boş sayfa → veri bitti
16 break
17
18 for item in batch:
19 articles.append({
20 "title": item["title"],
21 "url": item["url"],
22 })
23
24 page += 1
25
26 return articles4.4 Markdown Satırlarını Oluşturmak
1def build_medium_rows(articles):
2 if not articles:
3 return "_No articles found._"
4
5 rows = []
6 for a in articles:
7 # Publication varsa italik parantez içinde göster
8 pub = f" *({a['publication']})*" if a.get("publication") else ""
9 rows.append(f'- [{a["title"]}]({a["url"]}){pub}')
10
11 return "\n".join(rows)
12
13
14def build_devto_rows(articles):
15 if not articles:
16 return "_No articles found._"
17
18 return "\n".join(
19 f'- [{a["title"]}]({a["url"]})' for a in articles
20 )4.5 README'yi Güncellemek
Regex ile marker'lar arasındaki bloğu bulup yeni içerikle değiştiriyoruz. re.DOTALL flag'i . karakterinin yeni satırları da eşlemesini sağlıyor; bu olmadan çok satırlı bloklar yakalanmaz.
1def replace_section(content, start_marker, end_marker, new_body):
2 pattern = re.compile(
3 rf"{re.escape(start_marker)}.*?{re.escape(end_marker)}",
4 re.DOTALL,
5 )
6 replacement = f"{start_marker}\n{new_body}\n{end_marker}"
7 return pattern.sub(replacement, content)
8
9
10def main():
11 print("📡 Fetching Medium articles...")
12 medium_articles = fetch_medium_articles()
13 print(f" ✅ {len(medium_articles)} articles fetched.")
14
15 print("📡 Fetching dev.to articles...")
16 devto_articles = fetch_devto_articles()
17 print(f" ✅ {len(devto_articles)} articles fetched.")
18
19 with open(README_PATH, "r", encoding="utf-8") as f:
20 content = f.read()
21
22 content = replace_section(
23 content, MEDIUM_START, MEDIUM_END, build_medium_rows(medium_articles)
24 )
25 content = replace_section(
26 content, DEVTO_START, DEVTO_END, build_devto_rows(devto_articles)
27 )
28
29 with open(README_PATH, "w", encoding="utf-8") as f:
30 f.write(content)
31
32 print("✅ README.md updated successfully!")
33
34
35if __name__ == "__main__":
36 main()5. GitHub Actions Workflow Dosyası ⚙️

Workflow dosyası .github/workflows/ klasörü altına konur. GitHub bu klasörü otomatik olarak tarar ve YAML dosyalarını workflow olarak işler.
1.github/
2└── workflows/
3 └── update-articles.ymlDosya içeriği;
1name: 📝 Update Blog Articles
2
3on:
4 schedule:
5 - cron: "0 6 * * *" # Her gün 06:00 UTC — Türkiye saatiyle 09:00
6 workflow_dispatch: # Actions sekmesinden manuel tetikleme için
7
8jobs:
9 update-readme:
10 name: Fetch & Update Articles
11 runs-on: ubuntu-latest
12
13 steps:
14 - name: 🔄 Checkout Repository
15 uses: actions/checkout@v4
16
17 - name: 🐍 Set up Python
18 uses: actions/setup-python@v5
19 with:
20 python-version: "3.11"
21
22 - name: 📦 Install Dependencies
23 run: pip install requests feedparser
24
25 - name: 🚀 Run Article Fetcher
26 run: python scripts/fetch_articles.py
27
28 - name: 💾 Commit & Push Changes
29 run: |
30 git config --local user.email "github-actions[bot]@users.noreply.github.com"
31 git config --local user.name "github-actions[bot]"
32 git add README.md
33 git diff --staged --quiet || git commit -m "📝 Auto-update: Latest blog articles [$(date +'%Y-%m-%d')]"
34 git pushactions/checkout@v4 — Workflow'un çalıştığı sanal makineye (ubuntu-latest) reponuzu kopyalar. Olmadan script README.md'yi bulamaz.
actions/setup-python@v5 — Python 3.11 ortamını kurar. GitHub Actions makinelerinde Python önceden kurulu olsa da versiyonu sabitlemek daha güvenlidir.
pip install requests feedparser — İki bağımlılığı kurar.
python scripts/fetch_articles.py — Scripti çalıştırır. Bu adım README.md dosyasını günceller ama henüz commit atmaz.
Commit & Push adımı
1git diff --staged --quiet || git commit -m "..."git diff --staged --quiet README'de değişiklik yoksa “sıfır” döndürür yani “exit” kodu döndürür; || operatörü sayesinde değişiklik varsa commit atılır, yoksa hiçbir şey yapılmaz.
💡 Cron sözdizimi: 0 6 * * * → dakika=0, saat=6, her gün, her ay, haftanın her günü. UTC kullandığını unutmayın; Türkiye saatiyle GMT+3, yani 09:00'da çalışır. Farklı bir saat için crontab.guru aracını kullanabilirsiniz.
6. Manuel Tetikleme 🖱️

Cron job her sabah çalışır ama yeni bir makale yayınladığınızda beklemek istemeyebilirsiniz. workflow_dispatch tetikleyicisi sayesinde istediğiniz an çalıştırabilirsiniz:
- GitHub'da reponuza gidin
- Üstteki Actions sekmesine tıklayın
- Sol menüde 📝 Update Blog Articles workflow'una tıklayın
- Sağda beliren Run workflow butonuna tıklayın
- Açılan dropdown'da Run workflow butonuna tekrar tıklayın
Birkaç saniye içinde workflow başlar. Sol menüde sarı dönen bir ikon çıkar, tamamlanınca yeşil ✅ olur. README'nize baktığınızda yeni makale listesini göreceksiniz.
7. Demo 👀

Bu yazıda anlattığım sistemin çalışan örneğini kendi GitHub profilimde görebilirsiniz. Medium ve dev.to makaleleri her sabah otomatik olarak güncelleniyor.
Kaynak kodun tamamınafetch_articles.py ve update-articles.yml dahil aşağıdaki repodan ulaşabilirsiniz:
👉 github.com/yasinatesim/yasinatesim
📬 Geri bildirim
Makaleyi yazarken, yazım denetimi ve araştırma için “Claude Opus 4.6 Thinking” modelini kullandım. Resimleri üretmek için ise “Gemini 3.1 Flash Image Preview (Nano Banana 2)” modelini kullandım.
Yazı ile ilgili tavsiye, öneri, eleştirileri dikkate alıyorum. İletişime geçmek isterseniz bana websitemdeki sosyal medya adreslerimden veya Linkedin üzerinden ulaşabilirsiniz.
Sevgiyle kalın, Yasin 🤗
📚Makaleyi Yazarken Kullandığım Kaynaklar
Dokumanlar
- GitHub Actions — Workflow syntax
- GitHub Actions — Events (schedule, workflow_dispatch)
- dev.to API Docs
- feedparser Docs
Araçlar
- crontab.guru — Cron ifadelerini görsel olarak test edin
- GitHub Secrets Docs — API key'leri güvenle saklayın
