patx/gitman

improve gitman pages routing to be closer to githubs

Commit 4a4774d · patx · 2026-05-22T03:45:57-04:00

Changeset
4a4774db0564eee09966db0db39ebbe1ca1dcd64
Parents
cdfc519763953bcab60fe43e756dc09c65da2053

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/README.md b/README.md
index d1f4a99..f82318d 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ The web UI reads directly from Git for source browsing, raw file downloads, comm
 
 Issues, comments, stars, contributors, forks, and pull request records are stored in SQLite. Forking creates a bare clone of the source repository. Pull requests compare a source ref against a target branch, and maintainers can close or merge them from the browser.
 
-Pages-style static hosting is driven by the Git repository contents. A user site repository named `<username>.<GITMAN_PAGES_DOMAIN>` is served from its repository root at `https://<username>.<GITMAN_PAGES_DOMAIN>/`; project Pages can be enabled in repository settings and are served from that repository's `docs/` directory. Any published Pages repository can advertise a custom domain with a DNS TXT verification record: user sites use a root `CNAME` file, and project sites use `docs/CNAME`.
+Pages-style static hosting is driven by the Git repository contents. A user site repository named `<username>.<GITMAN_PAGES_DOMAIN>` is served from its repository root at `https://<username>.<GITMAN_PAGES_DOMAIN>/`; project Pages can be enabled in repository settings and are served from that repository's `docs/` directory. Any published Pages repository can advertise a custom domain with a DNS TXT verification record: user sites use a root `CNAME` file, and project sites use `docs/CNAME`. A custom domain on the user site also serves that user's enabled project Pages at `https://<custom-domain>/<repo>/`.
 
 ## Configuration
 
diff --git a/app.py b/app.py
index 2249d84..fc05fcc 100644
--- a/app.py
+++ b/app.py
@@ -2783,6 +2783,8 @@ def pages_settings_context(repo):
         "txt_name": "",
         "txt_value": "",
     }
+    if not context["custom_domain_ready"]:
+        return context
 
     domain, cname_error = read_cname_domain_for_repo(repo)
     context["cname_domain"] = domain
@@ -2984,7 +2986,10 @@ def pages_response_for_request_host():
 
     site, registered_domain = custom_domain_site_for_host(host)
     if site:
-        return pages_response_for_custom_domain_repo(site["repo"])
+        repo = site["repo"]
+        if is_user_site_repo(repo):
+            return pages_response_for_owner(repo["owner_username"])
+        return pages_response_for_custom_domain_repo(repo)
     if registered_domain:
         return pages_not_found_response()
     return None
diff --git a/templates/repo_settings.tpl b/templates/repo_settings.tpl
index 68a3f40..c9fbff2 100644
--- a/templates/repo_settings.tpl
+++ b/templates/repo_settings.tpl
@@ -59,6 +59,7 @@
           <p><strong>Published</strong> <small class="muted">(from source: `docs/`)</small></p>
         % else:
           <p><strong>Not published</strong> <small class="muted">(from source: `docs/`)</small></p>
+          <p class="muted">Publish this repository to serve static files from `docs/`.</p>
         % end
       </div>
       <button class="button" type="submit">{{"Unpublish Pages" if pages_settings["docs_enabled"] else "Publish Pages"}}</button>
@@ -67,7 +68,9 @@
   <p class="muted"><strong>Pages URL:</strong> <a href="{{pages_settings['url']}}">{{pages_settings["url"]}}</a> <small class="muted">(from source: `root`)</small></p>
   % end
 
-  % if pages_settings["cname_error"]:
+  % if not pages_settings["custom_domain_ready"]:
+    <p class="muted"><strong>Custom Domain:</strong> Publish Pages before configuring a custom domain.</p>
+  % elif pages_settings["cname_error"]:
     <p class="alert">{{pages_settings["cname_error"]}}</p>
   % elif pages_settings["cname_domain"]:
     % custom_domain = pages_settings["custom_domain"]
diff --git a/tests/test_app.py b/tests/test_app.py
index 0790770..3134843 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -1698,6 +1698,16 @@ def test_custom_pages_domain_requires_dns_txt_verification_and_current_cname(iso
     assert response.status_code == 200
     assert "Alice Custom Site" in response.text
 
+    isolated_app.create_repository(owner, "project", "")
+    project_path = isolated_app.repo_path("alice", "project")
+    commit_file(project_path, "docs/index.html", "<h1>Alice Project Docs</h1>\n", message="docs index", user="alice")
+    response = alice_client.post("/alice/project/settings", {"action": "update_pages", "pages_docs_enabled": "1"})
+    assert response.status_code == 200
+
+    response = alice_client.get("/project/", headers={"Host": "www.example.com"})
+    assert response.status_code == 200
+    assert "Alice Project Docs" in response.text
+
     isolated_app.create_repository(attacker, "bob.gitman.io", "")
     attacker_path = isolated_app.repo_path("bob", "bob.gitman.io")
     commit_file(attacker_path, "index.html", "<h1>Bob Site</h1>\n", message="site index", user="bob")
@@ -1742,28 +1752,31 @@ def test_project_custom_pages_domain_requires_publish_and_serves_docs_root(isola
 
     settings_response = client.get("/alice/project/settings")
     assert settings_response.status_code == 200
-    assert "docs/CNAME" in settings_response.text
-    assert "docs.example.com" in settings_response.text
+    assert "Publish Pages before configuring a custom domain." in settings_response.text
+    assert "docs.example.com" not in settings_response.text
     assert "root.example.com" not in settings_response.text
-    assert "Publish Pages before verifying this domain." in settings_response.text
-
-    custom_domain = isolated_app.get_custom_domain_for_repo(project["id"], "docs.example.com")
-    expected_txt = isolated_app.custom_domain_txt_value(custom_domain["verification_token"])
-    assert expected_txt in settings_response.text
+    assert "_gitman-pages.docs.example.com" not in settings_response.text
+    assert "Verify DNS" not in settings_response.text
+    assert isolated_app.get_custom_domain_for_repo(project["id"], "docs.example.com") is None
 
-    response = client.get("/", headers={"Host": "docs.example.com"})
-    assert response.status_code == 404
-
-    monkeypatch.setattr(isolated_app, "resolve_dns_txt", lambda record_name: [expected_txt])
     response = client.post("/alice/project/settings", {"action": "verify_custom_domain"})
     assert response.status_code == 200
     assert "Publish Pages before verifying a custom domain." in response.text
-    assert isolated_app.get_custom_domain_for_repo(project["id"], "docs.example.com")["verified_at"] is None
+    assert isolated_app.get_custom_domain_for_repo(project["id"], "docs.example.com") is None
+
+    response = client.get("/", headers={"Host": "docs.example.com"})
+    assert "Project Custom Docs" not in response.text
 
     response = client.post("/alice/project/settings", {"action": "update_pages", "pages_docs_enabled": "1"})
     assert response.status_code == 200
     assert "Pages settings updated." in response.text
+    assert "docs/CNAME" in response.text
+    assert "docs.example.com" in response.text
+    custom_domain = isolated_app.get_custom_domain_for_repo(project["id"], "docs.example.com")
+    expected_txt = isolated_app.custom_domain_txt_value(custom_domain["verification_token"])
+    assert expected_txt in response.text
 
+    monkeypatch.setattr(isolated_app, "resolve_dns_txt", lambda record_name: [expected_txt])
     response = client.post("/alice/project/settings", {"action": "verify_custom_domain"})
     assert response.status_code == 200
     assert "Custom domain verified." in response.text