patx/gitman
improve gitman pages routing to be closer to githubs
Commit 4a4774d · patx · 2026-05-22T03:45:57-04:00
Comments
No comments yet.
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