hub.sr.ht/hubsrht/builds.py

113 lines
4.2 KiB
Python

import email.utils
import json
import yaml
from flask import url_for
from hubsrht.services 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("builds.sr.ht", 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:
# https://github.com/libgit2/pygit2/pull/1019
# Will be useful for mailing lists shared by many repositories
return None
repo = (SourceRepo.query
.filter(SourceRepo.project_id == project.id)
.filter(func.lower(SourceRepo.name) == prefix.lower())).one_or_none()
if not repo:
return None
if repo.repo_type != RepoType.git:
# TODO: support for hg.sr.ht
return None
manifests = git.get_manifests(repo.owner, repo.remote_id)
if not manifests:
return None
ids = []
for key, value in manifests.items():
tool_key = f"hub.sr.ht:builds.sr.ht:{key}"
lists.patchset_set_tool(ml.owner, ml.name, payload["id"],
tool_key, "pending", f"build pending: {key}")
manifest = Manifest(yaml.safe_load(value))
# TODO: https://todo.sr.ht/~sircmpwn/builds.sr.ht/291
task = Task({
"_apply_patch": f"""echo Applying patch from lists.sr.ht
git config --global user.name 'builds.sr.ht'
git config --global user.email builds@sr.ht
cd {repo.name}
curl --no-progress-meter {ml.url()}/patches/{payload["id"]}/mbox >/tmp/{payload["id"]}.patch
git am -3 /tmp/{payload["id"]}.patch"""
})
manifest.tasks.insert(0, task)
trigger = next((t for t in manifest.triggers
if t.action == TriggerAction.email), None)
if not trigger:
trigger = Trigger({
"action": TriggerAction.email,
"condition": TriggerCondition.always,
})
manifest.triggers.append(trigger)
trigger.condition = TriggerCondition.always
root = get_origin("hub.sr.ht", external=True)
details = fernet.encrypt(json.dumps({
"mailing_list": ml.id,
"patchset_id": payload["id"],
"key": tool_key,
"name": key,
}).encode()).decode()
manifest.triggers.append(Trigger({
"action": TriggerAction.webhook,
"condition": TriggerCondition.always,
"url": root + url_for("webhooks.build_complete", details=details),
}))
addrs = email.utils.getaddresses(trigger.attrs.get("to", ""))
reply_to = payload.get("reply_to")
if reply_to:
submitter = email.utils.parseaddr(reply_to)
else:
submitter = email.utils.parseaddr(payload["submitter"])
if submitter not in addrs:
addrs.append(submitter)
trigger.attrs["to"] = ", ".join([email.utils.formataddr(a) for a in addrs])
cc = email.utils.getaddresses(trigger.attrs.get("cc", ""))
if not ml.posting_addr() in cc:
cc.append(('', ml.posting_addr()))
trigger.attrs["cc"] = ", ".join([email.utils.formataddr(a) for a in cc])
trigger.attrs["in_reply_to"] = payload["message_id"]
version = payload["version"]
if version == 1:
version = ""
else:
version = f" v{version}"
b = builds.submit_build(project.owner, manifest,
f"""[{subject}][0]{version} from [{submitter[0]}][1]
[0]: {ml.url()}/patches/{payload["id"]}
[1]: mailto:{submitter[1]}""", tags=[repo.name, "patches", key])
ids.append(b["id"])
build_url = f"{buildsrht}/{project.owner.canonical_name}/job/{b['id']}"
lists.patchset_set_tool(ml.owner, ml.name, payload["id"],
tool_key, "waiting", f"[#{b['id']}]({build_url}) running {key}")
return ids