Sfoglia il codice sorgente

fix: compare-changes links translate SSH git remotes to HTTPS (ssh://git@host:port/owner/repo.git and git@host:owner/repo forms; SSH-only ports dropped)

clanker 1 mese fa
parent
commit
c83d384c39
2 ha cambiato i file con 89 aggiunte e 1 eliminazioni
  1. 55 1
      src/Services/TaskService.vala
  2. 34 0
      test/web_stack_test.py

+ 55 - 1
src/Services/TaskService.vala

@@ -403,7 +403,61 @@ namespace ClankerSaddle.Services {
                 return "";
             }
             string base_branch = ((!)task).branch_from ?? ((!)project).default_branch;
-            return @"$(((!)project).git_url)/compare/$(base_branch)...$(((!)task).task_branch)";
+            return @"$(https_git_url(((!)project).git_url))/compare/$(base_branch)...$(((!)task).task_branch)";
+        }
+
+        /**
+         * Translates a project's git remote into its browsable HTTPS form
+         * so compare links open in the web UI instead of an SSH checkout:
+         * `ssh://git@host:port/owner/repo.git` and `git@host:owner/repo(.git)`
+         * both become `https://host/owner/repo` (the port is dropped —
+         * SSH-only ports like fabrica.unitatem.net:222 have no HTTPS
+         * meaning). Anything already http(s) (or unrecognised) is kept
+         * as-is, minus a trailing `.git`.
+         */
+        public static string https_git_url(string git_url) {
+            string url = git_url.strip();
+            if (url.has_prefix("http://") || url.has_prefix("https://")) {
+                return url.has_suffix(".git") ? url.substring(0, url.length - 4) : url;
+            }
+
+            // ssh://git@host[:port]/owner/repo.git
+            if (url.has_prefix("ssh://")) {
+                string rest = url.substring("ssh://".length);
+                if (rest.has_prefix("git@")) {
+                    rest = rest.substring("git@".length);
+                }
+                int slash = rest.index_of("/");
+                if (slash < 0) {
+                    return url;
+                }
+                string host_port = rest.substring(0, slash);
+                string path = rest.substring(slash);
+                if (host_port.contains(":")) {
+                    // SSH-only ports (fabrica.unitatem.net:222) carry no
+                    // HTTPS meaning — the browse host is plain.
+                    host_port = host_port.split(":")[0];
+                }
+                if (path.has_suffix(".git")) {
+                    path = path.substring(0, path.length - 4);
+                }
+                return @"https://$(host_port)$(path)";
+            }
+
+            // git@host:owner/repo(.git) — scp-style (the colon sits before
+            // any path slash)
+            int at = url.index_of("@");
+            int colon = url.index_of(":");
+            int slash = url.index_of("/");
+            if (at >= 0 && colon > at && (slash < 0 || colon < slash)) {
+                string host = url.substring(at + 1, colon - at - 1);
+                string path = url.substring(colon + 1);
+                if (path.has_suffix(".git")) {
+                    path = path.substring(0, path.length - 4);
+                }
+                return @"https://$(host)/$(path)";
+            }
+            return url;
         }
 
         /** Records that `task_id` must not start until `depends_on_task_id` is done. */

+ 34 - 0
test/web_stack_test.py

@@ -478,6 +478,40 @@ def main():
               and question.get("show_body") is True,
               "answered question shows body + selected answer")
 
+        print("== B5: compare-changes SSH -> HTTPS ==")
+        def diff_url_for_project(git_url, suffix):
+            state = slot_public(auth, "/projects", "projects")
+            act(auth, state["create"], {"name": "diff-%s" % suffix, "git_url": git_url})
+            project_id = int([p["id"] for p in slot_public(auth, "/projects", "projects")["projects"]
+                              if p["name"] == "diff-%s" % suffix][0])
+            conn = db()
+            conn.execute("insert into tasks (project_id, title, status, task_branch,"
+                         " branch_from, created_at) values (?, ?, 'working', 'feature/x',"
+                         " 'main', strftime('%s','now'))", (project_id, "Diff %s" % suffix))
+            conn.commit()
+            task_id = conn.execute("select max(id) from tasks").fetchone()[0]
+            conn.execute("insert into sessions (task_id, saddle_id, started_at, ended_at,"
+                         " outcome) values (?, ?, strftime('%s','now'), strftime('%s','now'),"
+                         " 'done')", (task_id, fake_id))
+            conn.commit()
+            session = conn.execute("select max(id) from sessions").fetchone()[0]
+            conn.close()
+            saddle_api("POST", "/api/saddle/%d/update" % fake_id, fake_token,
+                       {"type": "block", "session_id": session, "data": {
+                           "question": "Review the diff", "type": "review"}})
+            return task_state(auth, task_id).get("review_diff_url") or ""
+
+        check(diff_url_for_project("ssh://fabrica.unitatem.net:222/clanker/clanker-saddle.git",
+                                   "fabrica")
+              == "https://fabrica.unitatem.net/clanker/clanker-saddle/compare/main...feature/x",
+              "ssh://git@host:port/owner/repo.git translates (port dropped)")
+        check(diff_url_for_project("git@github.com:acme/widgets.git", "scp")
+              == "https://github.com/acme/widgets/compare/main...feature/x",
+              "git@host:owner/repo.git translates to the browse host")
+        check(diff_url_for_project("https://example.com/plain/repo", "plain")
+              == "https://example.com/plain/repo/compare/main...feature/x",
+              "HTTPS URLs pass through unchanged")
+
         print("== feature 5: rules ==")
         rules_state = slot_public(auth, "/rules", "rules")
         for name, body in [