You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
4.4 KiB

import email.utils
import json
import random
import yaml
from flask import url_for
from import builds, git, lists
from hubsrht.types import SourceRepo, RepoType
from sqlalchemy import func
from srht.config import get_origin
from srht.crypto import fernet
def submit_patchset(ml, payload):
buildsrht = get_origin("", external=True, default=None)
if not buildsrht:
return None
from buildsrht.manifest import Manifest, Task
from buildsrht.manifest import Trigger, TriggerAction, TriggerCondition
project = ml.project
subject = payload["subject"]
prefix = payload["prefix"]
if not prefix:
# TODO: More sophisticated matching is possible
# - test if patch is applicable to a repo; see the following:
# Will be useful for mailing lists shared by many repositories
return None
repo = (SourceRepo.query
.filter(SourceRepo.project_id ==
.filter(func.lower( == prefix.lower())).one_or_none()
if not repo:
return None
if repo.repo_type != RepoType.git:
# TODO: support for
return None
manifests = git.get_manifests(repo.owner,
if not manifests:
return None
if len(manifests) > 4:
keys = list(manifests.keys())
manifests = { key: manifests[key] for key in keys[:4] }
ids = []
version = payload["version"]
if version == 1:
version = ""
version = f" v{version}"
reply_to = payload.get("reply_to")
if reply_to:
submitter = email.utils.parseaddr(reply_to)
submitter = email.utils.parseaddr(payload["submitter"])
build_note = f"""[{subject}][0]{version} from [{submitter[0]}][1]
[0]: {ml.url()}/patches/{payload["id"]}
[1]: mailto:{submitter[1]}"""
for key, value in manifests.items():
tool_key = f"{key}"
lists.patchset_set_tool(ml.owner,, payload["id"],
tool_key, "pending", f"build pending: {key}")
manifest = Manifest(yaml.safe_load(value))
# TODO: Maybe we should email the user with the error details?
task = Task({
"_apply_patch": f"""echo Applying patch from
git config --global ''
git config --global
cd {}
curl -sS {ml.url()}/patches/{payload["id"]}/mbox >/tmp/{payload["id"]}.patch
git am -3 /tmp/{payload["id"]}.patch"""
manifest.tasks.insert(0, task)
if not manifest.environment:
manifest.environment = {}
manifest.environment.setdefault("BUILD_SUBMITTER", "")
manifest.environment.setdefault("BUILD_REASON", "patchset")
manifest.environment.setdefault("PATCHSET_ID", payload["id"])
# Add webhook trigger
root = get_origin("", external=True)
details = fernet.encrypt(json.dumps({
"patchset_id": payload["id"],
"key": tool_key,
"name": key,
"user": project.owner.canonical_name,
"action": TriggerAction.webhook,
"condition": TriggerCondition.always,
"url": root + url_for("webhooks.build_complete", details=details),
b = builds.submit_build(project.owner, manifest, build_note,
tags=[, "patches", key], execute=False)
build_url = f"{buildsrht}/{project.owner.canonical_name}/job/{b['id']}"
lists.patchset_set_tool(ml.owner,, payload["id"],
tool_key, "waiting", f"[#{b['id']}]({build_url}) running {key}")
trigger = Trigger({
"condition": TriggerCondition.always,
trigger.attrs["to"] = email.utils.formataddr(submitter)
trigger.attrs["cc"] = ml.posting_addr()
trigger.attrs["in_reply_to"] = payload["message_id"]
builds.create_group(project.owner, ids, build_note, [trigger.to_dict()])
return ids