Rig up Mercurial support

This commit is contained in:
Drew DeVault 2020-04-06 13:21:13 -04:00
parent ab1cb8e381
commit 89a2e3ac9f
5 changed files with 160 additions and 7 deletions

View File

@ -1,6 +1,6 @@
from flask import Blueprint, render_template, request, redirect, url_for
from hubsrht.projects import ProjectAccess, get_project
from hubsrht.services import git
from hubsrht.services import git, hg
from hubsrht.types import Event, EventType
from hubsrht.types import Project, RepoType, Visibility
from srht.database import db
@ -18,9 +18,13 @@ def summary_GET(owner, project_name):
summary_error = False
if project.summary_repo_id is not None:
repo = project.summary_repo
assert repo.repo_type != RepoType.hg # TODO
try:
summary = git.get_readme(owner, repo.name)
if repo.repo_type == RepoType.git:
summary = git.get_readme(owner, repo.name)
elif repo.repo_type == RepoType.hg:
summary = hg.get_readme(owner, repo.name)
else:
assert False
except:
summary = None
summary_error = True

View File

@ -1,6 +1,6 @@
from flask import Blueprint, render_template, request, redirect, url_for
from hubsrht.projects import ProjectAccess, get_project
from hubsrht.services import git
from hubsrht.services import git, hg
from hubsrht.types import Event, EventType
from hubsrht.types import RepoType, SourceRepo
from srht.database import db
@ -56,7 +56,14 @@ def git_new_GET(owner, project_name):
@sources.route("/<owner>/<project_name>/hg/new")
@loginrequired
def hg_new_GET(owner, project_name):
pass # TODO
owner, project = get_project(owner, project_name, ProjectAccess.write)
# TODO: Pagination
repos = hg.get_repos(owner)
repos = sorted(repos, key=lambda r: r["updated"], reverse=True)
return render_template("sources-select.html",
view="new-resource", vcs="hg",
owner=owner, project=project, repos=repos,
existing=[]) # TODO: Fetch existing repos for this project
@sources.route("/<owner>/<project_name>/git/new", methods=["POST"])
@loginrequired
@ -117,6 +124,65 @@ def git_new_POST(owner, project_name):
return redirect(url_for("projects.summary_GET",
owner=owner.canonical_name, project_name=project.name))
@sources.route("/<owner>/<project_name>/hg/new", methods=["POST"])
@loginrequired
def hg_new_POST(owner, project_name):
owner, project = get_project(owner, project_name, ProjectAccess.write)
valid = Validation(request)
if "create" in valid:
hg_repo = hg.create_repo(owner, valid)
if not valid.ok:
repos = hg.get_repos(owner)
return render_template("sources-select.html",
view="new-resource", vcs="hg",
owner=owner, project=project, repos=repos,
existing=[], **valid.kwargs)
else:
repo_name = None
for field in valid.source:
if field.startswith("existing-"):
repo_name = field[len("existing-"):]
break
if not repo_name:
search = valid.optional("search")
repos = hg.get_repos(owner)
# TODO: Search properly
repos = filter(lambda r: search.lower() in r["name"].lower(), repos)
repos = sorted(repos, key=lambda r: r["updated"], reverse=True)
# TODO: Fetch existing repos for this project
return render_template("sources-select.html",
view="new-resource", vcs="hg",
owner=owner, project=project, repos=repos,
existing=[], search=search)
hg_repo = hg.get_repo(owner, repo_name)
repo = SourceRepo()
repo.remote_id = hg_repo["id"]
repo.project_id = project.id
repo.owner_id = owner.id
repo.name = hg_repo["name"]
repo.description = hg_repo["description"]
repo.repo_type = RepoType.hg
db.session.add(repo)
db.session.flush()
event = Event()
event.event_type = EventType.source_repo_added
event.source_repo_id = repo.id
event.project_id = project.id
event.user_id = project.owner_id
db.session.add(event)
hg.ensure_user_webhooks(owner)
#hg.ensure_repo_webhooks(owner, repo.name) # TODO
db.session.commit()
return redirect(url_for("projects.summary_GET",
owner=owner.canonical_name, project_name=project.name))
@sources.route("/<owner>/<project_name>/sources/manage")
@loginrequired
def manage_GET(owner, project_name):
@ -187,7 +253,12 @@ def delete_POST(owner, project_name, repo_id):
valid = Validation(request)
delete_remote = valid.optional("delete-remote") == "on"
if delete_remote:
git.delete_repo(owner, repo.name)
if repo.repo_type == RepoType.git:
git.delete_repo(owner, repo.name)
elif repo.repo_type == RepoType.hg:
hg.delete_repo(owner, repo.name)
else:
assert False
db.session.commit()
return redirect(url_for("projects.summary_GET",

View File

@ -27,6 +27,27 @@ def git_repo():
elif event == "repo:post-update":
raise NotImplementedError()
@csrf_bypass
@webhooks.route("/webhooks/hg-repo", methods=["POST"])
def hg_repo():
event = request.headers.get("X-Webhook-Event")
payload = json.loads(request.data.decode("utf-8"))
if event == "repo:update":
repo = (SourceRepo.query
.filter(SourceRepo.remote_id == payload["id"])
.filter(SourceRepo.repo_type == RepoType.hg)
.one_or_none())
if not repo:
return "I don't recognize that repository.", 404
repo.name = payload["name"]
repo.description = payload["description"]
db.session.commit()
return f"Updated local:{repo.id}/remote:{repo.remote_id}. Thanks!", 200
elif event == "repo:delete":
raise NotImplementedError()
elif event == "repo:post-update":
raise NotImplementedError()
@csrf_bypass
@webhooks.route("/webhooks/mailing-list", methods=["POST"])
def mailing_list():

View File

@ -5,6 +5,7 @@ from srht.api import ensure_webhooks, get_authorization, get_results
from srht.config import get_origin
_gitsrht = get_origin("git.sr.ht", external=True, default=None)
_hgsrht = get_origin("hg.sr.ht", external=True, default=None)
_listsrht = get_origin("lists.sr.ht", external=True, default=None)
_todosrht = get_origin("todo.sr.ht", external=True, default=None)
origin = get_origin("hub.sr.ht")
@ -81,6 +82,61 @@ class GitService(SrhtService):
url = f"{_gitsrht}/api/{user.canonical_name}/repos/{repo_name}/webhooks"
ensure_webhooks(user, url, config)
class HgService(SrhtService):
def __init__(self):
super().__init__()
def get_repos(self, user):
return get_results(f"{_hgsrht}/api/repos", user)
def get_repo(self, user, repo_name):
r = self.session.get(f"{_hgsrht}/api/repos/{repo_name}",
headers=get_authorization(user))
if r.status_code != 200:
raise Exception(r.text)
return r.json()
def get_readme(self, user, repo_name):
# TODO: Cache?
r = self.session.get(f"{_hgsrht}/api/repos/{repo_name}/raw/README.md",
headers=get_authorization(user))
if r.status_code == 404:
return ""
elif r.status_code != 200:
raise Exception(r.text)
return r.text
def create_repo(self, user, valid):
name = valid.require("name")
description = valid.optional("description")
if not valid.ok:
return None
return self.post(user, valid, f"{_hgsrht}/api/repos", {
"name": name,
"description": description,
"visibility": "public", # TODO: Should this be different?
})
def delete_repo(self, user, repo_name):
r = self.session.delete(f"{_hgsrht}/api/repos/{repo_name}",
headers=get_authorization(user))
if r.status_code != 204:
raise Exception(r.text)
def ensure_user_webhooks(self, user):
config = {
origin + url_for("webhooks.hg_repo"):
["repo:update", "repo:delete"],
}
ensure_webhooks(user, f"{_hgsrht}/api/user/webhooks", config)
def ensure_repo_webhooks(self, user, repo_name):
config = {
origin + url_for("webhooks.hg_repo"): ["repo:post-update"],
}
url = f"{_hgsrht}/api/{user.canonical_name}/repos/{repo_name}/webhooks"
ensure_webhooks(user, url, config)
class ListService(SrhtService):
def get_lists(self, user):
return get_results(f"{_listsrht}/api/lists", user)
@ -169,5 +225,6 @@ class TodoService(SrhtService):
})
git = GitService()
hg = HgService()
lists = ListService()
todo = TodoService()

View File

@ -5,7 +5,7 @@ from srht.config import get_origin
from srht.database import Base
_gitsrht = get_origin("git.sr.ht", external=True, default=None)
_hgsrht = get_origin("git.sr.ht", external=True, default=None)
_hgsrht = get_origin("hg.sr.ht", external=True, default=None)
class RepoType(Enum):
git = "git"