diff options
-rw-r--r-- | .github/workflows/repositories.yml | 46 | ||||
-rwxr-xr-x | bin/repositories-checker.py | 111 |
2 files changed, 157 insertions, 0 deletions
diff --git a/.github/workflows/repositories.yml b/.github/workflows/repositories.yml new file mode 100644 index 0000000..3c3a0c3 --- /dev/null +++ b/.github/workflows/repositories.yml @@ -0,0 +1,46 @@ +name: repositories +on: + pull_request: + paths: + - 'files/overlays/repositories.xml' + +jobs: + validate-content: + runs-on: ubuntu-latest + steps: + + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v3 + with: + python-version: '3.x' + cache: 'pip' + - name: Install pip dependencies + run: pip install lxml + + - name: Check repositories.xml + run: | + BASE_REF=$(git merge-base --fork-point origin/${{ github.base_ref }}) + python bin/repositories-checker.py <(git show ${BASE_REF}:files/overlays/repositories.xml) files/overlays/repositories.xml + + validate-schema: + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Download repositories.xml schema + run: wget https://gitweb.gentoo.org/data/xml-schema.git/plain/repositories.xsd + + - name: Prepare xmllint annotator + uses: korelstar/xmllint-problem-matcher@v1 + + - name: Lint repositories.xml + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: ./files/overlays/repositories.xml + xml-schema-file: ./repositories.xsd diff --git a/bin/repositories-checker.py b/bin/repositories-checker.py new file mode 100755 index 0000000..7d7cae3 --- /dev/null +++ b/bin/repositories-checker.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +""" +Copyright (C) 2022 Arthur Zamarin <arthurzam@gentoo.org> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. +""" + +from http.client import HTTPSConnection +import sys +from typing import Iterator, Tuple +from urllib.parse import quote_plus +from lxml import etree + + +ok_flag = True + + +def output_xml_error(xml: etree._Element, title: str, content: str): + start = xml.sourceline + end = start + len(etree.tostring(xml).strip().split(b'\n')) - 1 + print(f'::error file={sys.argv[2]},line={start},endLine={end},title={title}::{content}') + + global ok_flag + ok_flag = False + + +def output_xml_warning(xml: etree._Element, title: str, content: str): + start = xml.sourceline + end = start + len(etree.tostring(xml).strip().split()) + print(f'::warning file={sys.argv[2]},line={start},endLine={end},title={title}::{content}') + + +class Overlay: + def __init__(self, xml: etree._Element): + self.xml = xml + + if (repo_name := xml.find('./name')) is not None: + self.repo_name = repo_name.text + else: + self.repo_name = '' + output_xml_error(xml, 'Missing overlay name', 'Missing tag "name" for the overlay') + + if (owner_email := xml.find('./owner/email')) is not None: + self.owner_email = owner_email.text + else: + output_xml_error(xml.find('./owner'), 'Missing owner email', 'Missing tag "email" for the overlay\'s owner') + + def check_details(self, client: HTTPSConnection): + if not getattr(self, 'owner_email', None): + return + try: + client.request("GET", f"/rest/user?names={quote_plus(self.owner_email)}") + resp = client.getresponse() + resp.read() + if resp.status != 200: + output_xml_error(self.xml.find('owner/email'), 'Unknown email', f'email address "{self.owner_email}" not found at bugzilla') + else: + print(f'\033[92m\u2713 repo="{self.repo_name}" <{self.owner_email}>\033[0m') + except Exception: + output_xml_warning(self.xml.find('owner/email'), 'Failed check against bugzilla', + f'Checking for bugzilla email [{self.owner_email}] failed') + + def __hash__(self): + return hash(self.repo_name) + + def __eq__(self, o) -> bool: + return isinstance(o, Overlay) and o.repo_name == self.repo_name + + +def read_repositories(file: str) -> Iterator[Overlay]: + return map(Overlay, etree.parse(file).findall('./repo')) + + +def check_maintainers(overlays: Iterator[Overlay]) -> Iterator[Overlay]: + try: + client = HTTPSConnection('bugs.gentoo.org') + for m in overlays: + m.check_details(client) + finally: + client.close() + + +def check_sorted(curr: Tuple[Overlay], adds: Iterator[Overlay]): + for addition in adds: + index = curr.index(addition) + if index > 0 and curr[index - 1].repo_name >= addition.repo_name: + output_xml_error(addition.xml, 'Unsorted overlay list', f'overlay "{addition.repo_name}" in wrong place') + elif index < len(curr) and curr[index + 1].repo_name <= addition.repo_name: + output_xml_error(addition.xml, 'Unsorted overlay list', f'overlay "{addition.repo_name}" in wrong place') + + +if __name__ == '__main__': + base = tuple(read_repositories(sys.argv[1])) + current = tuple(read_repositories(sys.argv[2])) + additions = frozenset(current).difference(base) + + check_maintainers(additions) + check_sorted(current, additions) + sys.exit(int(not ok_flag)) |