public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] data/api:master commit in: .github/workflows/, bin/
@ 2022-04-12 18:38 Arthur Zamarin
  0 siblings, 0 replies; 2+ messages in thread
From: Arthur Zamarin @ 2022-04-12 18:38 UTC (permalink / raw
  To: gentoo-commits

commit:     0980709210144ef8c5ee9d39987aa15906b7dcdb
Author:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
AuthorDate: Tue Apr 12 18:37:38 2022 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Tue Apr 12 18:37:38 2022 +0000
URL:        https://gitweb.gentoo.org/data/api.git/commit/?id=09807092

Add CI workflow for checking repositories.xml

For pull requests touching repositories.xml, check that added emails
are associated with a bugzilla account, it matches schema, it is
sorted, and some must have fields are defined.

Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 .github/workflows/repositories.yml |  46 +++++++++++++++
 bin/repositories-checker.py        | 111 +++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+)

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))


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [gentoo-commits] data/api:master commit in: .github/workflows/, bin/
@ 2023-02-27 19:31 Arthur Zamarin
  0 siblings, 0 replies; 2+ messages in thread
From: Arthur Zamarin @ 2023-02-27 19:31 UTC (permalink / raw
  To: gentoo-commits

commit:     a415817eb8b23db8aa7b99684ba7db33b279e2e1
Author:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
AuthorDate: Mon Feb 27 19:27:51 2023 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Mon Feb 27 19:30:40 2023 +0000
URL:        https://gitweb.gentoo.org/data/api.git/commit/?id=a415817e

bin/repositories-checker.py: various improvements and fixes

- Add `--github` option to output in github actions format,
  otherwise use colored term output.
- If "-" is passed as base xml, assume we are doing full xml check.
  In this mode I skip checking maintainer emails since it might use
  too much API calls.
- Fix typing
- Improve error message for bas repos sorting (explain what is bad)

Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 .github/workflows/repositories.yml |  2 +-
 bin/repositories-checker.py        | 61 +++++++++++++++++++++++++-------------
 2 files changed, 41 insertions(+), 22 deletions(-)

diff --git a/.github/workflows/repositories.yml b/.github/workflows/repositories.yml
index 3f09609..dae070f 100644
--- a/.github/workflows/repositories.yml
+++ b/.github/workflows/repositories.yml
@@ -23,7 +23,7 @@ jobs:
     - 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
+        python bin/repositories-checker.py --github <(git show ${BASE_REF}:files/overlays/repositories.xml) files/overlays/repositories.xml
 
   validate-schema:
     runs-on: ubuntu-latest

diff --git a/bin/repositories-checker.py b/bin/repositories-checker.py
index 7d7cae3..2810bc3 100755
--- a/bin/repositories-checker.py
+++ b/bin/repositories-checker.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 
 """
-Copyright (C) 2022 Arthur Zamarin <arthurzam@gentoo.org>
+Copyright (C) 2022-2023 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
@@ -17,20 +17,24 @@ 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 argparse
 import sys
-from typing import Iterator, Tuple
+from http.client import HTTPSConnection
+from typing import Iterable, Tuple
 from urllib.parse import quote_plus
-from lxml import etree
 
+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}')
+    if args.github:
+        end = start + len(etree.tostring(xml).strip().split(b'\n')) - 1
+        print(f'::error file={args.current},line={start},endLine={end},title={title}::{content}')
+    else:
+        print(f'\033[91m{args.current}:{start} - {title} - {content}\033[0m')
 
     global ok_flag
     ok_flag = False
@@ -38,8 +42,11 @@ def output_xml_error(xml: etree._Element, title: str, content: str):
 
 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}')
+    if args.github:
+        end = start + len(etree.tostring(xml).strip().split())
+        print(f'::warning file={args.current},line={start},endLine={end},title={title}::{content}')
+    else:
+        print(f'\033[93m{args.current}:{start} - {title} - {content}\033[0m')
 
 
 class Overlay:
@@ -79,11 +86,11 @@ class Overlay:
         return isinstance(o, Overlay) and o.repo_name == self.repo_name
 
 
-def read_repositories(file: str) -> Iterator[Overlay]:
+def read_repositories(file: str) -> Iterable[Overlay]:
     return map(Overlay, etree.parse(file).findall('./repo'))
 
 
-def check_maintainers(overlays: Iterator[Overlay]) -> Iterator[Overlay]:
+def check_maintainers(overlays: Iterable[Overlay]):
     try:
         client = HTTPSConnection('bugs.gentoo.org')
         for m in overlays:
@@ -92,20 +99,32 @@ def check_maintainers(overlays: Iterator[Overlay]) -> Iterator[Overlay]:
         client.close()
 
 
-def check_sorted(curr: Tuple[Overlay], adds: Iterator[Overlay]):
+def check_sorted(curr: Tuple[Overlay, ...], adds: Iterable[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')
+        repo_name = addition.repo_name
+        if index > 0 and curr[index - 1].repo_name.lower() >= repo_name.lower():
+            output_xml_error(addition.xml, 'Unsorted overlay list',
+                f"overlay {repo_name!r} in wrong place: {repo_name!r} isn't before {curr[index - 1].repo_name!r}")
+        elif index + 1 < len(curr) and curr[index + 1].repo_name.lower() <= repo_name.lower():
+            output_xml_error(addition.xml, 'Unsorted overlay list',
+                f"overlay {repo_name!r} in wrong place: {repo_name!r} isn't after {curr[index + 1].repo_name!r}")
 
 
 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)
+    parser = argparse.ArgumentParser(description='Check repositories.xml')
+    parser.add_argument('base', help='Original repositories.xml, pass "-" to perform full check')
+    parser.add_argument('current', help='Current repositories.xml')
+    parser.add_argument('--github', help='print errors in GitHub Actions format', action='store_true')
+    args = parser.parse_args()
+
+    current = tuple(read_repositories(args.current))
+
+    if args.base != '-':
+        base = tuple(read_repositories(args.base))
+        additions = frozenset(current).difference(base)
+        check_maintainers(additions)
+        check_sorted(current, additions)
+    else:
+        check_sorted(current, current)
     sys.exit(int(not ok_flag))


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2023-02-27 19:31 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-02-27 19:31 [gentoo-commits] data/api:master commit in: .github/workflows/, bin/ Arthur Zamarin
  -- strict thread matches above, loose matches on Subject: below --
2022-04-12 18:38 Arthur Zamarin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox