Initial commit: GitHub Release Monitor CLI tool

CLI tool that tracks GitHub releases or tags from user-defined repositories
and exposes them as a subscribable RSS 2.0 feed.

Features:
- Track releases or tags per repository
- SQLite storage with automatic deduplication
- RSS 2.0 feed with markdown-stripped release notes
- Built-in HTTP server for local feed access
- Rate-limit-aware GitHub API client
- 64 passing tests
This commit is contained in:
2026-07-28 11:56:10 +02:00
commit 2a5f11bd9b
18 changed files with 2117 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
"""HTTP server for serving the RSS feed."""
from http.server import BaseHTTPRequestHandler
from urllib.parse import urlparse
from src.db import get_connection
from src.rss import generate_feed, generate_index_html
def create_handler(db_path: str):
"""Factory that returns an HTTP request handler class bound to a DB path."""
class FeedHandler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urlparse(self.path)
path = parsed.path.rstrip("/") or "/"
if path == "/feed.xml":
self._serve_feed()
elif path == "/health":
self._serve_health()
elif path == "/":
self._serve_index()
else:
self.send_response(404)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(b"Not Found")
def _serve_feed(self):
conn = get_connection(db_path)
try:
from src.db import get_all_entries
entries = get_all_entries(conn)
feed_xml = generate_feed(entries, self._base_url())
finally:
conn.close()
self.send_response(200)
self.send_header("Content-Type", "application/rss+xml; charset=utf-8")
self.end_headers()
self.wfile.write(feed_xml.encode("utf-8"))
def _serve_index(self):
html = generate_index_html(self._base_url())
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(html.encode("utf-8"))
def _serve_health(self):
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(b"OK")
def _base_url(self) -> str:
host = self.server.server_address[0]
port = self.server.server_address[1]
return f"http://{host}:{port}"
def log_message(self, format, *args):
"""Suppress default request logging."""
pass
return FeedHandler