summaryrefslogtreecommitdiffstats
path: root/gitsrht
diff options
context:
space:
mode:
authorIvan Habunek <ivan@habunek.com>2018-11-18 13:25:04 +0100
committerDrew DeVault <sir@cmpwn.com>2018-11-18 08:54:07 -0500
commit4502aa711cf239b018646aa46ee283619b04ab55 (patch)
treeeca54743d818b70f6d8afb8927bf449fd5ac5f3c /gitsrht
parent28434c24d6bf47c38ed8284f4a917908a6763c39 (diff)
Implement RSS feeds for refs and log
https://todo.sr.ht/~sircmpwn/git.sr.ht/100
Diffstat (limited to 'gitsrht')
-rw-r--r--gitsrht/blueprints/repo.py64
-rw-r--r--gitsrht/rss.py101
-rw-r--r--gitsrht/templates/log.html11
-rw-r--r--gitsrht/templates/refs.html10
4 files changed, 180 insertions, 6 deletions
diff --git a/gitsrht/blueprints/repo.py b/gitsrht/blueprints/repo.py
index 6ba09ff..476a140 100644
--- a/gitsrht/blueprints/repo.py
+++ b/gitsrht/blueprints/repo.py
@@ -15,6 +15,7 @@ from gitsrht.redis import redis
from gitsrht.git import Repository as GitRepository, commit_time, annotate_tree
from gitsrht.git import diffstat
from gitsrht.types import User, Repository, Redirect
+from gitsrht.rss import generate_feed
from io import BytesIO
from pygments import highlight
from pygments.lexers import guess_lexer_for_filename, TextLexer
@@ -278,6 +279,15 @@ def collect_refs(git_repo):
refs[_ref.commit.id.hex].append(_ref)
return refs
+def get_log(git_repo, commit, commits_per_page=20):
+ commits = list()
+ for commit in git_repo.walk(commit.id, pygit2.GIT_SORT_TIME):
+ commits.append(commit)
+ if len(commits) >= commits_per_page + 1:
+ break
+
+ return commits
+
@repo.route("/<owner>/<repo>/log", defaults={"ref": None, "path": ""})
@repo.route("/<owner>/<repo>/log/<ref>", defaults={"path": ""})
@repo.route("/<owner>/<repo>/log/<ref>/<path:path>")
@@ -291,17 +301,31 @@ def log(owner, repo, ref, path):
if from_id:
commit = git_repo.get(from_id)
- commits_per_page = 20
- commits = list()
- for commit in git_repo.walk(commit.id, pygit2.GIT_SORT_TIME):
- commits.append(commit)
- if len(commits) >= commits_per_page + 1:
- break
+ commits = get_log(git_repo, commit)
return render_template("log.html", view="log",
owner=owner, repo=repo, ref=ref, path=path,
commits=commits, refs=refs)
+
+@repo.route("/<owner>/<repo>/log/rss.xml", defaults={"ref": None})
+@repo.route("/<owner>/<repo>/log/<ref>/rss.xml")
+def log_rss(owner, repo, ref):
+ owner, repo = get_repo_or_redir(owner, repo)
+ with GitRepository(repo.path) as git_repo:
+ commit, ref = lookup_ref(git_repo, ref)
+ commits = get_log(git_repo, commit)
+
+ repo_name = f"{repo.owner.canonical_name}/{repo.name}"
+ title = f"{repo_name} log"
+ description = f"Git log for {repo_name} {ref}"
+ link = cfg("git.sr.ht", "origin") + url_for("repo.log",
+ owner=repo.owner.canonical_name,
+ repo=repo.name,
+ ref=ref if ref != "master" else None).replace("%7E", "~") # hack
+
+ return generate_feed(repo, commits, title, link, description)
+
@repo.route("/<owner>/<repo>/commit/<ref>")
def commit(owner, repo, ref):
owner, repo = get_repo_or_redir(owner, repo)
@@ -383,6 +407,34 @@ def refs(owner, repo):
git_repo=git_repo, isinstance=isinstance, pygit2=pygit2,
page=page + 1, total_pages=total_pages)
+
+@repo.route("/<owner>/<repo>/refs/rss.xml")
+def refs_rss(owner, repo):
+ owner, repo = get_repo_or_redir(owner, repo)
+ with GitRepository(repo.path) as git_repo:
+ references = [
+ git_repo.references[name]
+ for name in git_repo.references
+ if name.startswith("refs/tags/")
+ ]
+
+ def _ref_sort_key(ref):
+ target = git_repo.get(ref.target)
+ author = target.author if hasattr(target, 'author') else target.tagger
+ return author.time + author.offset
+
+ references = sorted(references, key=_ref_sort_key, reverse=True)[:20]
+
+ repo_name = f"{repo.owner.canonical_name}/{repo.name}"
+ title = f"{repo_name} log"
+ description = f"Git refs for {repo_name}"
+ link = cfg("git.sr.ht", "origin") + url_for("repo.refs",
+ owner=repo.owner.canonical_name,
+ repo=repo.name).replace("%7E", "~") # hack
+
+ return generate_feed(repo, references, title, link, description)
+
+
@repo.route("/<owner>/<repo>/refs/<ref>")
def ref(owner, repo, ref):
owner, repo = get_repo_or_redir(owner, repo)
diff --git a/gitsrht/rss.py b/gitsrht/rss.py
new file mode 100644
index 0000000..e88841c
--- /dev/null
+++ b/gitsrht/rss.py
@@ -0,0 +1,101 @@
+import pygit2
+import xml.etree.ElementTree as ET
+from datetime import datetime, timedelta, timezone
+from flask import Response, url_for
+from gitsrht.git import Repository as GitRepository
+from srht.config import cfg
+
+# Date format used by RSS
+RFC_822_FORMAT = "%a, %d %b %Y %H:%M:%S %z"
+
+ORIGIN = cfg("git.sr.ht", "origin")
+
+def aware_time(author):
+ tzinfo = timezone(timedelta(minutes=author.offset))
+ return datetime.fromtimestamp(author.time, tzinfo)
+
+def ref_name(reference):
+ return reference.name.split("/")[-1]
+
+def ref_url(repo, reference):
+ return ORIGIN + url_for("repo.ref",
+ owner=repo.owner.canonical_name,
+ repo=repo.name,
+ ref=ref_name(reference)).replace("%7E", "~") # hack
+
+def commit_url(repo, commit):
+ return ORIGIN + url_for("repo.commit",
+ owner=repo.owner.canonical_name,
+ repo=repo.name,
+ ref=commit.id.hex).replace("%7E", "~") # hack
+
+def commit_title_description(commit):
+ """Split the commit message to title (first line) and the description
+ (remaining lines)."""
+ lines = commit.message.strip().split("\n")
+ if lines:
+ title = lines[0]
+ description = "\n".join(lines[1:]).strip().replace("\n", "<br />")
+ return title, description
+
+ # Empty message fallback
+ return commit.id.hex, ""
+
+def ref_to_item(repo, reference):
+ with GitRepository(repo.path) as git_repo:
+ target = git_repo.get(reference.target)
+
+ author = target.author if hasattr(target, 'author') else target.tagger
+ time = aware_time(author).strftime(RFC_822_FORMAT)
+ url = ref_url(repo, reference)
+ description = target.message.strip().replace("\n", "<br />")
+
+ element = ET.Element("item")
+ ET.SubElement(element, "title").text = ref_name(reference)
+ ET.SubElement(element, "description").text = description
+ ET.SubElement(element, "author").text = f"{author.email} ({author.name})"
+ ET.SubElement(element, "link").text = url
+ ET.SubElement(element, "guid").text = url
+ ET.SubElement(element, "pubDate").text = time
+
+ return element
+
+def commit_to_item(repo, commit):
+ time = aware_time(commit.author).strftime(RFC_822_FORMAT)
+ url = commit_url(repo, commit)
+ title, description = commit_title_description(commit)
+ author = f"{commit.author.email} ({commit.author.name})"
+
+ element = ET.Element("item")
+ ET.SubElement(element, "title").text = title
+ ET.SubElement(element, "description").text = description
+ ET.SubElement(element, "author").text = author
+ ET.SubElement(element, "link").text = url
+ ET.SubElement(element, "guid").text = url
+ ET.SubElement(element, "pubDate").text = time
+
+ return element
+
+def to_item(repo, item):
+ if isinstance(item, pygit2.Reference):
+ return ref_to_item(repo, item)
+
+ if isinstance(item, pygit2.Commit):
+ return commit_to_item(repo, item)
+
+ raise ValueError(f"Don't know how to convert {type(item)} to an RSS item.")
+
+def generate_feed(repo, items, title, link, description):
+ root = ET.Element("rss", version="2.0")
+ channel = ET.SubElement(root, "channel")
+
+ ET.SubElement(channel, "title").text = title
+ ET.SubElement(channel, "link").text = link
+ ET.SubElement(channel, "description").text = description
+ ET.SubElement(channel, "language").text = "en"
+
+ for item in items:
+ channel.append(to_item(repo, item))
+
+ xml = ET.tostring(root, encoding="UTF-8")
+ return Response(xml, mimetype='application/rss+xml')
diff --git a/gitsrht/templates/log.html b/gitsrht/templates/log.html
index fa273da..90740d3 100644
--- a/gitsrht/templates/log.html
+++ b/gitsrht/templates/log.html
@@ -3,6 +3,17 @@
{% block title %}
<title>{{repo.owner.canonical_name}}/{{repo.name}}: {{ref}} - {{cfg("sr.ht", "site-name")}} git</title>
{% endblock %}
+
+{% block head %}
+ <link rel="alternate"
+ title="{{ repo.owner.canonical_name }}/{{ repo.name }}: {{ ref }} log"
+ type="application/rss+xml"
+ href="{{ root }}{{ url_for('repo.log_rss',
+ owner=repo.owner.canonical_name,
+ repo=repo.name,
+ ref=ref if ref != "master" else None) }}">
+{% endblock %}
+
{% block content %}
<div class="container">
<div class="row">
diff --git a/gitsrht/templates/refs.html b/gitsrht/templates/refs.html
index a9f32a7..f63f025 100644
--- a/gitsrht/templates/refs.html
+++ b/gitsrht/templates/refs.html
@@ -3,6 +3,16 @@
{% block title %}
<title>{{repo.owner.canonical_name}}/{{repo.name}} refs - {{cfg("sr.ht", "site-name")}} git</title>
{% endblock %}
+
+{% block head %}
+ <link rel="alternate"
+ title="{{ repo.owner.canonical_name }}/{{ repo.name }} refs"
+ type="application/rss+xml"
+ href="{{ root }}{{ url_for('repo.refs_rss',
+ owner=repo.owner.canonical_name,
+ repo=repo.name) }}">
+{% endblock %}
+
{% block content %}
<div class="container">
<div class="row">