patx/gitman

Order profile repositories by latest update across push transports

Commit 44519f2 · patx · 2026-11-02T22:15:46-05:00

Changeset
44519f28ca9156c2314ad19e942a9dc96083c911
Parents
25623034a872ddf95543b9dd59e91db8a958cc43

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/app.py b/app.py
index fc0e6a9..357ddc2 100644
--- a/app.py
+++ b/app.py
@@ -10,6 +10,7 @@ import mimetypes
 import os
 import re
 import secrets
+import shlex
 import shutil
 import sqlite3
 import subprocess
@@ -1588,7 +1589,7 @@ def list_owned_repos(owner_id, limit=None, offset=0):
             LEFT JOIN repo_stars ON repo_stars.repo_id = repositories.id
             WHERE repositories.owner_id = ?
             GROUP BY repositories.id
-            ORDER BY repositories.updated_at DESC
+            ORDER BY repositories.updated_at DESC, repositories.id DESC
             {pagination}
             """,
             params,
@@ -1625,7 +1626,7 @@ def list_starred_repos(user_id, limit=None, offset=0):
             JOIN repositories ON repositories.id = repo_stars.repo_id
             JOIN users ON users.id = repositories.owner_id
             WHERE repo_stars.user_id = ?
-            ORDER BY repo_stars.created_at DESC
+            ORDER BY repositories.updated_at DESC, repositories.id DESC
             {pagination}
             """,
             params,
@@ -1747,7 +1748,10 @@ def install_repo_hooks(path):
     hooks_dir = path / "hooks"
     hooks_dir.mkdir(exist_ok=True)
     post_receive = hooks_dir / "post-receive"
-    post_receive.write_text(POST_RECEIVE_HOOK, encoding="utf-8")
+    activity_command = " ".join(shlex.quote(str(value)) for value in (
+        sys.executable, BASE_DIR / "repo_activity.py", DB_PATH.resolve(), path.parent.name, path.name,
+    ))
+    post_receive.write_text(POST_RECEIVE_HOOK.replace("#!/bin/sh\n", "#!/bin/sh\n" + activity_command + "\n", 1), encoding="utf-8")
     post_receive.chmod(0o755)
     install_quota_hook(path, MAX_REPO_BYTES)
 
diff --git a/repo_activity.py b/repo_activity.py
new file mode 100644
index 0000000..bbcc55e
--- /dev/null
+++ b/repo_activity.py
@@ -0,0 +1,22 @@
+"""Record accepted pushes, including those outside the HTTP application."""
+import datetime
+from pathlib import Path
+import sqlite3
+import sys
+
+
+def main():
+    database, owner, name = sys.argv[1:]
+    if not any(line.strip() for line in sys.stdin):
+        return
+    now = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
+    # Never create a new database if a deployment points at a missing store.
+    with sqlite3.connect(Path(database).as_uri() + "?mode=rw", uri=True, timeout=30) as connection:
+        connection.execute(
+            "UPDATE repositories SET updated_at=? WHERE name=? AND owner_id=(SELECT id FROM users WHERE username=?)",
+            (now, name, owner),
+        )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/test_app.py b/tests/test_app.py
index ed8d206..53275e6 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -1622,7 +1622,7 @@ def test_profile_repositories_paginate_owned_and_starred_tabs(isolated_app, monk
             conn.execute("UPDATE repositories SET updated_at = ? WHERE id = ?", (timestamp, repo["id"]))
             conn.execute(
                 "INSERT INTO repo_stars (repo_id, user_id, created_at) VALUES (?, ?, ?)",
-                (repo["id"], viewer["id"], timestamp),
+                (repo["id"], viewer["id"], f"2030-01-01T00:00:0{2-index}Z"),
             )
     client = WsgiClient(isolated_app.app)
 
@@ -2764,3 +2764,18 @@ def test_session_persists_fourteen_days_but_expires_on_server(isolated_app, monk
         clock.setattr(gitman.time, 'time', lambda: issued + 14 * 86400 + 1)
         # Replay the signed cookie even after browser expiry: server rejects it.
         assert client.get('/settings/profile').status_code == 303
+
+
+def test_direct_push_moves_repository_to_top_of_profile(isolated_app):
+    owner = create_user('alice')
+    isolated_app.create_repository(owner, 'older', '')
+    isolated_app.create_repository(owner, 'newer', '')
+    with isolated_app.db_connect() as conn:
+        conn.execute("UPDATE repositories SET updated_at='2000-01-01T00:00:00Z' WHERE name='older'")
+        conn.execute("UPDATE repositories SET updated_at='2001-01-01T00:00:00Z' WHERE name='newer'")
+    client = WsgiClient(isolated_app.app)
+    before = client.get('/alice').text
+    assert before.index('<strong>alice/newer') < before.index('<strong>alice/older')
+    commit_file(isolated_app.repo_path('alice', 'older'), 'README.md', 'New work')
+    after = client.get('/alice').text
+    assert after.index('<strong>alice/older') < after.index('<strong>alice/newer')