2020-04-23 19:52:03 +02:00
|
|
|
import email
|
2021-05-17 15:47:20 +02:00
|
|
|
import html
|
2020-03-31 17:17:19 +02:00
|
|
|
import json
|
2021-11-28 21:07:37 +01:00
|
|
|
import re
|
2020-04-08 20:33:16 +02:00
|
|
|
from datetime import datetime
|
2020-04-20 23:11:58 +02:00
|
|
|
from flask import Blueprint, request, current_app
|
2020-07-13 19:29:20 +02:00
|
|
|
from hubsrht.builds import submit_patchset
|
2021-11-28 21:07:37 +01:00
|
|
|
from hubsrht.services import git, todo, lists
|
|
|
|
from hubsrht.trailers import commit_trailers
|
2020-04-06 22:15:52 +02:00
|
|
|
from hubsrht.types import Event, EventType, MailingList, SourceRepo, RepoType
|
2020-04-29 19:25:49 +02:00
|
|
|
from hubsrht.types import Tracker, User, Visibility
|
2020-04-20 23:11:58 +02:00
|
|
|
from srht.config import get_origin
|
2020-07-13 22:14:59 +02:00
|
|
|
from srht.crypto import fernet, verify_request_signature
|
2020-03-31 17:17:19 +02:00
|
|
|
from srht.database import db
|
|
|
|
from srht.flask import csrf_bypass
|
2022-11-24 14:29:46 +01:00
|
|
|
from srht.validation import Validation
|
2020-04-23 19:52:03 +02:00
|
|
|
from urllib.parse import quote
|
2020-03-24 15:26:15 +01:00
|
|
|
|
|
|
|
webhooks = Blueprint("webhooks", __name__)
|
|
|
|
|
2020-04-20 23:11:58 +02:00
|
|
|
_gitsrht = get_origin("git.sr.ht", external=True, default=None)
|
|
|
|
_hgsrht = get_origin("hg.sr.ht", external=True, default=None)
|
|
|
|
_todosrht = get_origin("todo.sr.ht", external=True, default=None)
|
|
|
|
_listssrht = get_origin("lists.sr.ht", external=True, default=None)
|
|
|
|
|
2020-03-31 17:17:19 +02:00
|
|
|
@csrf_bypass
|
2020-04-06 22:15:52 +02:00
|
|
|
@webhooks.route("/webhooks/git-user/<int:user_id>", methods=["POST"])
|
|
|
|
def git_user(user_id):
|
2020-03-31 17:17:19 +02:00
|
|
|
event = request.headers.get("X-Webhook-Event")
|
2020-04-30 16:27:09 +02:00
|
|
|
payload = verify_request_signature(request)
|
2020-04-30 17:02:33 +02:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-04-06 22:15:52 +02:00
|
|
|
user = User.query.get(user_id)
|
|
|
|
if not user:
|
|
|
|
return "I don't recognize this user.", 404
|
|
|
|
|
2020-03-31 17:17:19 +02:00
|
|
|
if event == "repo:update":
|
2021-08-23 11:24:03 +02:00
|
|
|
repos = (SourceRepo.query
|
2020-03-31 17:17:19 +02:00
|
|
|
.filter(SourceRepo.remote_id == payload["id"])
|
2021-08-23 11:24:03 +02:00
|
|
|
.filter(SourceRepo.repo_type == RepoType.git))
|
|
|
|
summary = ""
|
|
|
|
for repo in repos:
|
|
|
|
repo.name = payload["name"]
|
|
|
|
repo.description = payload["description"]
|
2022-06-29 18:01:46 +02:00
|
|
|
repo.visibility = Visibility(payload["visibility"].upper())
|
2021-08-23 11:24:03 +02:00
|
|
|
repo.project.updated = datetime.utcnow()
|
|
|
|
db.session.commit()
|
|
|
|
summary += f"Updated local:{repo.id}/remote:{repo.remote_id}. Thanks!\n"
|
|
|
|
return summary, 200
|
2020-04-06 19:21:13 +02:00
|
|
|
elif event == "repo:delete":
|
2021-08-23 11:24:03 +02:00
|
|
|
repos = (SourceRepo.query
|
2020-04-06 22:15:52 +02:00
|
|
|
.filter(SourceRepo.remote_id == payload["id"])
|
2021-08-23 11:24:03 +02:00
|
|
|
.filter(SourceRepo.repo_type == RepoType.git))
|
2021-08-23 16:59:57 +02:00
|
|
|
summary = ""
|
2021-08-23 11:24:03 +02:00
|
|
|
for repo in repos:
|
|
|
|
if repo.project.summary_repo_id == repo.id:
|
|
|
|
repo.project.summary_repo = None
|
|
|
|
db.session.commit()
|
|
|
|
db.session.delete(repo)
|
|
|
|
repo.project.updated = datetime.utcnow()
|
2020-04-08 20:33:16 +02:00
|
|
|
db.session.commit()
|
2021-08-23 11:24:03 +02:00
|
|
|
summary += f"Deleted local:{repo.id}/remote:{repo.remote_id}. Thanks!\n"
|
|
|
|
return summary, 200
|
2020-04-06 22:15:52 +02:00
|
|
|
else:
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
@csrf_bypass
|
|
|
|
@webhooks.route("/webhooks/git-repo/<int:repo_id>", methods=["POST"])
|
|
|
|
def git_repo(repo_id):
|
|
|
|
event = request.headers.get("X-Webhook-Event")
|
2020-04-30 16:27:09 +02:00
|
|
|
payload = verify_request_signature(request)
|
2020-04-30 17:02:33 +02:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-04-06 22:15:52 +02:00
|
|
|
repo = SourceRepo.query.get(repo_id)
|
|
|
|
if not repo:
|
|
|
|
return "I don't recognize that repository.", 404
|
|
|
|
|
|
|
|
if event == "repo:post-update":
|
2020-04-29 16:34:03 +02:00
|
|
|
if not payload["refs"][0]["new"]:
|
|
|
|
return "Thanks!"
|
2020-04-20 23:11:58 +02:00
|
|
|
commit_sha = payload["refs"][0]["new"]["id"][:7]
|
|
|
|
commit_url = repo.url() + f"/commit/{commit_sha}"
|
|
|
|
commit_message = payload["refs"][0]["new"]["message"].split("\n")[0]
|
|
|
|
pusher_name = payload['pusher']['canonical_name']
|
2020-04-23 17:32:15 +02:00
|
|
|
pusher_url = f"{_gitsrht}/{pusher_name}"
|
2020-04-20 23:11:58 +02:00
|
|
|
repo_name = repo.owner.canonical_name + "/" + repo.name
|
|
|
|
|
|
|
|
pusher = current_app.oauth_service.lookup_user(payload['pusher']['name'])
|
|
|
|
|
|
|
|
event = Event()
|
|
|
|
event.event_type = EventType.external_event
|
|
|
|
event.source_repo_id = repo.id
|
|
|
|
event.project_id = repo.project_id
|
|
|
|
event.user_id = pusher.id
|
|
|
|
|
|
|
|
event.external_source = "git.sr.ht"
|
|
|
|
event.external_summary = (
|
|
|
|
f"<a href='{commit_url}'>{commit_sha}</a> " +
|
2021-05-17 15:47:20 +02:00
|
|
|
f"<code>{html.escape(commit_message)}</code>")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_summary_plain = f"{commit_sha} - {commit_message}"
|
2020-04-20 23:11:58 +02:00
|
|
|
event.external_details = (
|
|
|
|
f"<a href='{pusher_url}'>{pusher_name}</a> pushed to " +
|
|
|
|
f"<a href='{repo.url()}'>{repo_name}</a> git")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_details_plain = f"{pusher_name} pushed to {repo_name} git"
|
|
|
|
event.external_url = commit_url
|
2020-04-20 23:11:58 +02:00
|
|
|
|
2020-04-29 18:23:31 +02:00
|
|
|
repo.project.updated = datetime.utcnow()
|
2020-04-20 23:11:58 +02:00
|
|
|
db.session.add(event)
|
|
|
|
db.session.commit()
|
2021-11-28 21:07:37 +01:00
|
|
|
|
|
|
|
for ref in payload["refs"]:
|
|
|
|
old = (ref["old"] or {}).get("id")
|
|
|
|
new = (ref["new"] or {}).get("id")
|
2022-03-21 10:28:10 +01:00
|
|
|
if not old or not new:
|
|
|
|
continue # New ref, or ref deleted
|
2021-11-28 21:07:37 +01:00
|
|
|
for commit in reversed(git.log(pusher, repo, old, new)):
|
|
|
|
for trailer, value in commit_trailers(commit["message"]):
|
|
|
|
_handle_commit_trailer(trailer, value, pusher, repo, commit)
|
|
|
|
|
2020-04-20 23:11:58 +02:00
|
|
|
return "Thanks!"
|
2020-04-06 22:15:52 +02:00
|
|
|
else:
|
2020-04-06 19:21:13 +02:00
|
|
|
raise NotImplementedError()
|
|
|
|
|
2021-11-28 21:07:37 +01:00
|
|
|
_ticket_url_re = re.compile(
|
|
|
|
rf"""
|
|
|
|
^
|
|
|
|
{re.escape(_todosrht)}
|
|
|
|
/(?P<owner>~[a-z_][a-z0-9_-]+)
|
|
|
|
/(?P<tracker>[\w.-]+)
|
|
|
|
/(?P<ticket>\d+)
|
|
|
|
$
|
|
|
|
""",
|
|
|
|
re.VERBOSE,
|
2021-12-09 14:30:43 +01:00
|
|
|
) if _todosrht else None
|
2021-11-28 21:07:37 +01:00
|
|
|
|
|
|
|
def _handle_commit_trailer(trailer, value, pusher, repo, commit):
|
2021-12-09 14:30:43 +01:00
|
|
|
if not _todosrht:
|
|
|
|
return
|
|
|
|
|
2022-07-25 14:16:04 +02:00
|
|
|
if trailer == "Closes":
|
|
|
|
resolution = "closed"
|
|
|
|
elif trailer == "Fixes":
|
2021-11-28 21:07:37 +01:00
|
|
|
resolution = "fixed"
|
|
|
|
elif trailer == "Implements":
|
|
|
|
resolution = "implemented"
|
|
|
|
elif trailer == "References":
|
|
|
|
resolution = None
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
match = _ticket_url_re.match(value.strip())
|
|
|
|
if not match:
|
|
|
|
return
|
|
|
|
|
|
|
|
commit_message = html.escape(commit["message"].split("\n")[0])
|
|
|
|
commit_author = html.escape(commit["author"]["name"].strip())
|
|
|
|
commit_sha = commit["id"][:7]
|
|
|
|
commit_url = repo.url() + f"/commit/{commit_sha}"
|
2021-11-29 14:55:54 +01:00
|
|
|
comment = f"""\
|
|
|
|
*{commit_author} referenced this ticket in commit [{commit_sha}].*
|
|
|
|
|
|
|
|
[{commit_sha}]: {commit_url} "{commit_message}"\
|
|
|
|
"""
|
2021-11-28 21:07:37 +01:00
|
|
|
try:
|
2022-01-14 21:09:41 +01:00
|
|
|
existing_comments = todo.get_ticket_comments(
|
|
|
|
user=pusher,
|
|
|
|
owner=match["owner"],
|
|
|
|
tracker=match["tracker"],
|
|
|
|
ticket=int(match["ticket"]),
|
|
|
|
)
|
|
|
|
if comment in existing_comments:
|
|
|
|
# avoid duplicate comments
|
|
|
|
return
|
2021-11-28 21:07:37 +01:00
|
|
|
todo.update_ticket(
|
|
|
|
user=pusher,
|
|
|
|
owner=match["owner"],
|
|
|
|
tracker=match["tracker"],
|
|
|
|
ticket=int(match["ticket"]),
|
2021-11-29 14:55:54 +01:00
|
|
|
comment=comment,
|
2021-11-28 21:07:37 +01:00
|
|
|
resolution=resolution,
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
# invalid ticket or pusher does not have triage access, ignore
|
|
|
|
pass
|
|
|
|
|
2020-04-06 19:21:13 +02:00
|
|
|
@csrf_bypass
|
2020-04-06 22:15:52 +02:00
|
|
|
@webhooks.route("/webhooks/hg-user/<int:user_id>", methods=["POST"])
|
|
|
|
def hg_user(user_id):
|
2020-04-06 19:21:13 +02:00
|
|
|
event = request.headers.get("X-Webhook-Event")
|
2020-04-30 16:27:09 +02:00
|
|
|
payload = verify_request_signature(request)
|
2020-04-30 17:02:33 +02:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-04-06 22:15:52 +02:00
|
|
|
user = User.query.get(user_id)
|
|
|
|
if not user:
|
|
|
|
return "I don't recognize this user.", 404
|
|
|
|
|
2020-04-06 19:21:13 +02:00
|
|
|
if event == "repo:update":
|
|
|
|
repo = (SourceRepo.query
|
2020-04-08 20:33:16 +02:00
|
|
|
.filter(SourceRepo.id == payload["id"])
|
2020-04-06 22:15:52 +02:00
|
|
|
.filter(SourceRepo.repo_type == RepoType.hg)).one_or_none()
|
2020-04-06 19:21:13 +02:00
|
|
|
if not repo:
|
|
|
|
return "I don't recognize that repository.", 404
|
2020-03-31 17:17:19 +02:00
|
|
|
repo.name = payload["name"]
|
|
|
|
repo.description = payload["description"]
|
2020-04-08 20:33:16 +02:00
|
|
|
repo.project.updated = datetime.utcnow()
|
2022-06-29 18:01:46 +02:00
|
|
|
repo.visibility = Visibility(payload["visibility"].upper())
|
2020-03-31 17:17:19 +02:00
|
|
|
db.session.commit()
|
|
|
|
return f"Updated local:{repo.id}/remote:{repo.remote_id}. Thanks!", 200
|
|
|
|
elif event == "repo:delete":
|
2020-04-08 20:33:16 +02:00
|
|
|
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 this hg repo.", 404
|
|
|
|
if repo.project.summary_repo_id == repo.id:
|
|
|
|
repo.project.summary_repo = None
|
|
|
|
db.session.commit()
|
|
|
|
db.session.delete(repo)
|
|
|
|
repo.project.updated = datetime.utcnow()
|
|
|
|
db.session.commit()
|
|
|
|
return f"Deleted local:{repo.id}/remote:{repo.remote_id}. Thanks!", 200
|
2020-04-06 22:15:52 +02:00
|
|
|
else:
|
2020-03-31 22:09:33 +02:00
|
|
|
raise NotImplementedError()
|
2020-03-25 15:08:29 +01:00
|
|
|
|
2020-03-31 17:17:19 +02:00
|
|
|
@csrf_bypass
|
2020-04-23 19:52:03 +02:00
|
|
|
@webhooks.route("/webhooks/mailing-list/<list_id>", methods=["POST"])
|
|
|
|
def mailing_list(list_id):
|
2020-03-31 17:17:19 +02:00
|
|
|
event = request.headers.get("X-Webhook-Event")
|
2020-04-30 16:27:09 +02:00
|
|
|
payload = verify_request_signature(request)
|
2020-04-30 17:02:33 +02:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-04-23 19:52:03 +02:00
|
|
|
ml = MailingList.query.get(list_id)
|
|
|
|
if not ml:
|
2020-03-31 17:17:19 +02:00
|
|
|
return "I don't recognize that mailing list.", 404
|
2020-04-23 19:52:03 +02:00
|
|
|
if event == "list:update":
|
2020-03-31 17:17:19 +02:00
|
|
|
ml.name = payload["name"]
|
|
|
|
ml.description = payload["description"]
|
2020-04-29 19:25:49 +02:00
|
|
|
if any(payload["permissions"]["nonsubscriber"]):
|
2022-06-30 23:05:56 +02:00
|
|
|
ml.visibility = Visibility.PUBLIC
|
2020-04-29 19:25:49 +02:00
|
|
|
else:
|
2022-06-30 23:05:56 +02:00
|
|
|
ml.visibility = Visibility.UNLISTED
|
2020-04-08 20:33:16 +02:00
|
|
|
ml.project.updated = datetime.utcnow()
|
2020-03-31 17:17:19 +02:00
|
|
|
db.session.commit()
|
|
|
|
return f"Updated local:{ml.id}/remote:{ml.remote_id}. Thanks!", 200
|
|
|
|
elif event == "list:delete":
|
2020-07-08 18:19:48 +02:00
|
|
|
db.session.delete(ml)
|
|
|
|
ml.project.updated = datetime.utcnow()
|
|
|
|
db.session.commit()
|
|
|
|
return f"Deleted local:{ml.id}/remote:{ml.remote_id}. Thanks!", 200
|
2020-03-31 22:09:33 +02:00
|
|
|
elif event == "post:received":
|
2020-04-23 19:52:03 +02:00
|
|
|
event = Event()
|
|
|
|
sender = payload["sender"]
|
2023-02-14 10:10:27 +01:00
|
|
|
sender_name = "Unknown"
|
2020-04-23 19:52:03 +02:00
|
|
|
if sender:
|
|
|
|
sender = current_app.oauth_service.lookup_user(sender['name'])
|
|
|
|
event.user_id = sender.id
|
2023-02-14 10:10:27 +01:00
|
|
|
sender_name = sender.canonical_name
|
2020-05-04 17:55:14 +02:00
|
|
|
sender_url = f"<a href='{_listssrht}/{sender.canonical_name}'>{sender.canonical_name}</a>"
|
2020-04-23 19:52:03 +02:00
|
|
|
else:
|
|
|
|
msg = email.message_from_string(payload["envelope"],
|
|
|
|
policy=email.policy.SMTP)
|
|
|
|
sender = email.utils.parseaddr(msg['From'])
|
2023-02-14 10:10:27 +01:00
|
|
|
sender_name = sender[0] if sender[0] else sender[1]
|
|
|
|
sender_url = sender_name
|
2020-04-23 19:52:03 +02:00
|
|
|
|
|
|
|
event.event_type = EventType.external_event
|
|
|
|
event.mailing_list_id = ml.id
|
|
|
|
event.project_id = ml.project_id
|
|
|
|
|
|
|
|
archive_url = ml.url() + f"/{quote(payload['message_id'])}"
|
|
|
|
subject = payload["subject"]
|
|
|
|
|
|
|
|
event.external_source = "todo.sr.ht"
|
|
|
|
event.external_summary = (
|
2021-05-17 15:47:20 +02:00
|
|
|
f"<a href='{archive_url}'>{html.escape(subject)}</a>")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_summary_plain = subject
|
2020-04-23 19:52:03 +02:00
|
|
|
event.external_details = (
|
|
|
|
f"{sender_url} via <a href='{ml.url()}'>{ml.name}</a>")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_details_plain = f"{sender_name} via {ml.name}"
|
|
|
|
event.external_url = archive_url
|
2020-04-23 19:52:03 +02:00
|
|
|
|
|
|
|
db.session.add(event)
|
|
|
|
db.session.commit()
|
|
|
|
return "Thanks!"
|
2020-03-31 22:09:33 +02:00
|
|
|
elif event == "patchset:received":
|
2022-11-24 14:29:46 +01:00
|
|
|
valid = Validation(request)
|
|
|
|
build_ids = submit_patchset(ml, payload, valid)
|
|
|
|
if not valid.ok:
|
|
|
|
emsg = f"{valid.errors[0].field}: {valid.errors[0].message}"
|
|
|
|
return f"Error submitting builds: {emsg}", 400
|
2020-07-13 19:29:20 +02:00
|
|
|
if build_ids:
|
|
|
|
return f"Submitted builds #{build_ids}. Thanks!"
|
|
|
|
else:
|
|
|
|
return "Thanks!"
|
2020-04-06 22:15:52 +02:00
|
|
|
else:
|
|
|
|
raise NotImplementedError()
|
2020-04-02 15:24:29 +02:00
|
|
|
|
|
|
|
@csrf_bypass
|
2020-04-06 22:15:52 +02:00
|
|
|
@webhooks.route("/webhooks/todo-user/<int:user_id>", methods=["POST"])
|
|
|
|
def todo_user(user_id):
|
2020-04-02 15:24:29 +02:00
|
|
|
event = request.headers.get("X-Webhook-Event")
|
2020-04-30 16:27:09 +02:00
|
|
|
payload = verify_request_signature(request)
|
2020-04-30 17:02:33 +02:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-04-06 22:15:52 +02:00
|
|
|
|
2020-04-08 20:33:16 +02:00
|
|
|
user = User.query.get(user_id)
|
2020-04-06 22:15:52 +02:00
|
|
|
if not user:
|
|
|
|
return "I don't recognize this tracker.", 404
|
|
|
|
|
2021-08-30 14:44:48 +02:00
|
|
|
summary = ""
|
2020-04-02 15:24:29 +02:00
|
|
|
if event == "tracker:update":
|
2021-08-30 14:44:48 +02:00
|
|
|
trackers = Tracker.query.filter(Tracker.remote_id == payload["id"])
|
|
|
|
for tracker in trackers:
|
|
|
|
tracker.name = payload["name"]
|
|
|
|
tracker.description = payload["description"]
|
2021-10-06 10:02:14 +02:00
|
|
|
if any(payload["default_access"]):
|
2022-06-30 23:05:56 +02:00
|
|
|
tracker.visibility = Visibility.PUBLIC
|
2021-08-30 14:44:48 +02:00
|
|
|
else:
|
2022-06-30 23:05:56 +02:00
|
|
|
tracker.visibility = Visibility.UNLISTED
|
2021-08-30 14:44:48 +02:00
|
|
|
tracker.project.updated = datetime.utcnow()
|
|
|
|
summary += f"Updated local:{tracker.id}/remote:{tracker.remote_id}\n"
|
2020-04-06 22:15:52 +02:00
|
|
|
db.session.commit()
|
2021-08-30 14:44:48 +02:00
|
|
|
return summary, 200
|
2020-04-06 22:15:52 +02:00
|
|
|
elif event == "tracker:delete":
|
2021-08-30 14:44:48 +02:00
|
|
|
trackers = Tracker.query.filter(Tracker.remote_id == payload["id"])
|
|
|
|
for tracker in trackers:
|
|
|
|
tracker.project.updated = datetime.utcnow()
|
|
|
|
db.session.delete(tracker)
|
|
|
|
db.session.commit()
|
|
|
|
summary += f"Deleted local:{tracker.id}/remote:{tracker.remote_id}\n"
|
|
|
|
return summary, 200
|
2020-04-06 22:15:52 +02:00
|
|
|
else:
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
@csrf_bypass
|
|
|
|
@webhooks.route("/webhooks/todo-tracker/<int:tracker_id>", methods=["POST"])
|
|
|
|
def todo_tracker(tracker_id):
|
|
|
|
event = request.headers.get("X-Webhook-Event")
|
2020-04-30 16:27:09 +02:00
|
|
|
payload = verify_request_signature(request)
|
2020-04-30 17:02:33 +02:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-04-06 22:15:52 +02:00
|
|
|
|
|
|
|
tracker = Tracker.query.get(tracker_id)
|
|
|
|
if not tracker:
|
|
|
|
return "I don't recognize this tracker.", 404
|
|
|
|
|
2020-04-08 20:33:16 +02:00
|
|
|
if event == "ticket:create":
|
2020-04-23 17:32:15 +02:00
|
|
|
event = Event()
|
|
|
|
submitter = payload["submitter"]
|
|
|
|
if submitter["type"] == "user":
|
|
|
|
event.user_id = current_app.oauth_service.lookup_user(submitter['name']).id
|
|
|
|
# TODO: Move this to a hub.sr.ht user page
|
|
|
|
submitter_url = f"{_todosrht}/{submitter['canonical_name']}"
|
|
|
|
submitter_url = f"<a href='{submitter_url}'>{submitter['canonical_name']}</a>"
|
|
|
|
elif submitter["type"] == "email":
|
|
|
|
submitter_url = f"{submitter['name']}"
|
|
|
|
else:
|
|
|
|
submitter_url = f"{submitter['external_id']}"
|
|
|
|
|
|
|
|
event.event_type = EventType.external_event
|
|
|
|
event.tracker_id = tracker.id
|
|
|
|
event.project_id = tracker.project_id
|
|
|
|
|
|
|
|
ticket_id = payload["id"]
|
|
|
|
ticket_url = tracker.url() + f"/{ticket_id}"
|
|
|
|
ticket_subject = payload["title"]
|
|
|
|
|
|
|
|
event.external_source = "todo.sr.ht"
|
|
|
|
event.external_summary = (
|
|
|
|
f"<a href='{ticket_url}'>#{ticket_id}</a> " +
|
2021-05-17 15:47:20 +02:00
|
|
|
f"{html.escape(ticket_subject)}")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_summary_plain = f"#{ticket_id} {ticket_subject}"
|
2020-04-23 17:32:15 +02:00
|
|
|
event.external_details = (
|
|
|
|
f"{submitter_url} filed ticket on " +
|
2020-04-23 18:08:00 +02:00
|
|
|
f"<a href='{tracker.url()}'>{tracker.name}</a> todo")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_details_plain = f"{submitter['canonical_name']} filed ticket on {tracker.name} todo"
|
|
|
|
event.external_url = ticket_url
|
2020-04-23 17:32:15 +02:00
|
|
|
|
|
|
|
db.session.add(event)
|
|
|
|
db.session.commit()
|
2020-04-23 18:08:00 +02:00
|
|
|
todo.ensure_ticket_webhooks(tracker, ticket_id)
|
2020-04-23 17:32:15 +02:00
|
|
|
return "Thanks!"
|
2020-04-02 15:24:29 +02:00
|
|
|
else:
|
|
|
|
raise NotImplementedError()
|
2020-04-06 22:15:52 +02:00
|
|
|
|
|
|
|
@csrf_bypass
|
|
|
|
@webhooks.route("/webhooks/todo-ticket/<int:tracker_id>/ticket", methods=["POST"])
|
|
|
|
def todo_ticket(tracker_id):
|
|
|
|
event = request.headers.get("X-Webhook-Event")
|
2020-04-30 16:27:09 +02:00
|
|
|
payload = verify_request_signature(request)
|
2020-04-30 17:02:33 +02:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-04-06 22:15:52 +02:00
|
|
|
|
|
|
|
tracker = Tracker.query.get(tracker_id)
|
|
|
|
if not tracker:
|
|
|
|
return "I don't recognize this tracker.", 404
|
|
|
|
|
|
|
|
if event == "event:create":
|
2020-04-23 18:08:00 +02:00
|
|
|
event = Event()
|
|
|
|
participant = payload["user"]
|
|
|
|
if participant["type"] == "user":
|
|
|
|
event.user_id = current_app.oauth_service.lookup_user(participant['name']).id
|
|
|
|
# TODO: Move this to a hub.sr.ht user page
|
|
|
|
participant_url = f"{_todosrht}/{participant['canonical_name']}"
|
|
|
|
participant_url = f"<a href='{participant_url}'>{participant['canonical_name']}</a>"
|
|
|
|
elif participant["type"] == "email":
|
|
|
|
participant_url = f"{participant['name']}"
|
|
|
|
else:
|
|
|
|
participant_url = f"{participant['external_id']}"
|
|
|
|
|
|
|
|
if not "comment" in payload["event_type"]:
|
|
|
|
return "Thanks!"
|
|
|
|
|
|
|
|
event.event_type = EventType.external_event
|
|
|
|
event.tracker_id = tracker.id
|
|
|
|
event.project_id = tracker.project_id
|
|
|
|
|
|
|
|
ticket_id = payload["ticket"]["id"]
|
|
|
|
ticket_url = tracker.url() + f"/{ticket_id}"
|
|
|
|
ticket_subject = payload["ticket"]["title"]
|
|
|
|
|
|
|
|
event.external_source = "todo.sr.ht"
|
|
|
|
event.external_summary = (
|
|
|
|
f"<a href='{ticket_url}'>#{ticket_id}</a> " +
|
2021-05-17 15:47:20 +02:00
|
|
|
f"{html.escape(ticket_subject)}")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_summary_plain = f"#{ticket_id} {ticket_subject}"
|
2020-04-23 18:08:00 +02:00
|
|
|
event.external_details = (
|
|
|
|
f"{participant_url} commented on " +
|
|
|
|
f"<a href='{tracker.url()}'>{tracker.name}</a> todo")
|
2023-02-14 10:10:27 +01:00
|
|
|
event.external_details_plain = f"{participant['canonical_name']} commented on {tracker.name} todo"
|
|
|
|
event.external_url = ticket_url
|
2020-04-23 18:08:00 +02:00
|
|
|
|
|
|
|
db.session.add(event)
|
|
|
|
db.session.commit()
|
|
|
|
return "Thanks!"
|
2020-04-06 22:15:52 +02:00
|
|
|
else:
|
|
|
|
raise NotImplementedError()
|
2020-07-13 22:14:59 +02:00
|
|
|
|
|
|
|
@csrf_bypass
|
|
|
|
@webhooks.route("/webhooks/build-complete/<details>", methods=["POST"])
|
|
|
|
def build_complete(details):
|
2021-02-20 15:23:25 +01:00
|
|
|
payload = verify_request_signature(request)
|
2021-02-20 15:58:27 +01:00
|
|
|
payload = json.loads(payload.decode('utf-8'))
|
2020-07-13 22:14:59 +02:00
|
|
|
details = fernet.decrypt(details.encode())
|
|
|
|
if not details:
|
|
|
|
return "Bad payload", 400
|
|
|
|
details = json.loads(details.decode())
|
|
|
|
ml = (MailingList.query
|
|
|
|
.filter(MailingList.id == details["mailing_list"])).one_or_none()
|
|
|
|
if not ml:
|
|
|
|
return "Unknown mailing list", 404
|
|
|
|
project = ml.project
|
2021-02-20 15:23:25 +01:00
|
|
|
if payload["owner"]["canonical_name"] != details["user"]:
|
|
|
|
return "Discarding webhook from unauthorized build", 401
|
2020-07-13 22:14:59 +02:00
|
|
|
|
|
|
|
buildsrht = get_origin("builds.sr.ht", external=True)
|
|
|
|
build_url = f"{buildsrht}/{project.owner.canonical_name}/job/{payload['id']}"
|
|
|
|
|
2022-11-09 11:53:43 +01:00
|
|
|
tool_details = f"[#{payload['id']}]({build_url}) {details['name']} {payload['status']}"
|
|
|
|
lists.patchset_update_tool(ml.owner, details["tool_id"],
|
2022-12-02 12:25:00 +01:00
|
|
|
payload["status"].upper(), tool_details)
|
2020-07-13 22:14:59 +02:00
|
|
|
|
|
|
|
return "Thanks!"
|