hub.sr.ht/hubsrht/blueprints/webhooks.py

442 lines
16 KiB
Python
Raw Normal View History

2020-04-23 19:52:03 +02:00
import email
import html
2020-03-31 17:17:19 +02:00
import json
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
2021-11-28 21:07:37 +01:00
import re
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
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
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
from hubsrht.types import Tracker, User, Visibility
2020-04-20 23:11:58 +02:00
from srht.config import get_origin
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
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":
repos = (SourceRepo.query
2020-03-31 17:17:19 +02:00
.filter(SourceRepo.remote_id == payload["id"])
.filter(SourceRepo.repo_type == RepoType.git))
summary = ""
for repo in repos:
repo.name = payload["name"]
repo.description = payload["description"]
repo.visibility = Visibility(payload["visibility"].upper())
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":
repos = (SourceRepo.query
2020-04-06 22:15:52 +02:00
.filter(SourceRepo.remote_id == payload["id"])
.filter(SourceRepo.repo_type == RepoType.git))
summary = ""
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()
db.session.commit()
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":
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> " +
f"<code>{html.escape(commit_message)}</code>")
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")
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()
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
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")
if not old or not new:
continue # New ref, or ref deleted
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
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()
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
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,
) if _todosrht else None
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
2021-11-28 21:07:37 +01:00
def _handle_commit_trailer(trailer, value, pusher, repo, commit):
if not _todosrht:
return
if trailer == "Closes":
resolution = "closed"
elif trailer == "Fixes":
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
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}"
comment = f"""\
*{commit_author} referenced this ticket in commit [{commit_sha}].*
[{commit_sha}]: {commit_url} "{commit_message}"\
"""
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
2021-11-28 21:07:37 +01:00
try:
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
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
2021-11-28 21:07:37 +01:00
todo.update_ticket(
user=pusher,
owner=match["owner"],
tracker=match["tracker"],
ticket=int(match["ticket"]),
comment=comment,
webhooks: update todo tickets with git commits Allow referencing tracker tickets in git commit messages via specific trailer words: Fixes: <ticket url> Implements: <ticket url> References: <ticket url> These must follow standard git trailer syntax. The trailers are extracted from commit messages with the function added in previous commit. The ticket url must point to a valid ticket. A comment will be inserted in the ticket with a back reference to the git commit and its original author. The comment will be made by the user who pushed the commit. E.g.: ~arkanoid REPORTED -> FIXED 9 seconds ago John Doe referenced this ticket in commit b4dc4c40. Open tickets referenced by a Fixes trailer will be resolved with the FIXED resolution. Open tickets referenced by an Implements trailer will be resolved with the IMPLEMENTED resolution. Caveats: * Only the 25 most recent commit messages will be considered when pushing long series. This should be a fairly sane limitation. * If the user pushing commits does not have triage/comment permissions on the bug tracker, nothing will happen. * Invalid/non-existent ticket urls are ignored. * When a git repository is part of more than one project, the webhook will run once per project and update the same ticket(s) once per project as well. * If an already resolved ticket is referenced by a Fixes or Implements trailer, only a comment will be added. Link: https://git-scm.com/docs/git-interpret-trailers Implements: https://todo.sr.ht/~sircmpwn/hub.sr.ht/55 Signed-off-by: Robin Jarry <robin@jarry.cc>
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
.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"]
repo.project.updated = datetime.utcnow()
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":
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"]
if any(payload["permissions"]["nonsubscriber"]):
ml.visibility = Visibility.PUBLIC
else:
ml.visibility = Visibility.UNLISTED
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"]
if sender:
sender = current_app.oauth_service.lookup_user(sender['name'])
event.user_id = sender.id
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'])
sender_url = sender[0] if sender[0] else sender[1]
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 = (
f"<a href='{archive_url}'>{html.escape(subject)}</a>")
2020-04-23 19:52:03 +02:00
event.external_details = (
f"{sender_url} via <a href='{ml.url()}'>{ml.name}</a>")
db.session.add(event)
db.session.commit()
return "Thanks!"
2020-03-31 22:09:33 +02:00
elif event == "patchset:received":
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
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
summary = ""
2020-04-02 15:24:29 +02:00
if event == "tracker:update":
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"]):
tracker.visibility = Visibility.PUBLIC
else:
tracker.visibility = Visibility.UNLISTED
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()
return summary, 200
2020-04-06 22:15:52 +02:00
elif event == "tracker:delete":
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
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> " +
f"{html.escape(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")
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> " +
f"{html.escape(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")
db.session.add(event)
db.session.commit()
return "Thanks!"
2020-04-06 22:15:52 +02:00
else:
raise NotImplementedError()
@csrf_bypass
@webhooks.route("/webhooks/build-complete/<details>", methods=["POST"])
def build_complete(details):
payload = verify_request_signature(request)
payload = json.loads(payload.decode('utf-8'))
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
if payload["owner"]["canonical_name"] != details["user"]:
return "Discarding webhook from unauthorized build", 401
buildsrht = get_origin("builds.sr.ht", external=True)
build_url = f"{buildsrht}/{project.owner.canonical_name}/job/{payload['id']}"
tool_details = f"[#{payload['id']}]({build_url}) {details['name']} {payload['status']}"
lists.patchset_update_tool(ml.owner, details["tool_id"],
payload["status"].upper(), tool_details)
return "Thanks!"