diff --git a/pyproject.toml b/pyproject.toml index a0d6e32..d082d89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "pkgupdates" description = "Tool to check ebuild pavkages for updates." -version = "0.1.0" +version = "0.2.0" license = "GPL-3.0-or-later" readme = "README.md" requires-python = "~=3.10" diff --git a/src/pkgupdates/__main__.py b/src/pkgupdates/__main__.py index 33443b0..ddacb5e 100644 --- a/src/pkgupdates/__main__.py +++ b/src/pkgupdates/__main__.py @@ -1,10 +1,29 @@ """Main Module""" +import argparse from .package import get_packages, check_package +from .settings import set_gh_rss_get + + +def process_arguments(): + """Process Arguments""" + parser = argparse.ArgumentParser( + description='Check for Gentoo overlay package updates' + ) + + parser.add_argument('--github-rss-get', + type=bool, + default=False, + required=False, + help='Get updates for Github over RSS instead of API') + args = parser.parse_args() + set_gh_rss_get(args.github_rss_get) def main(): """Main Function""" + + process_arguments() pkgs = get_packages() for pkg, meta in pkgs.items(): check_package(pkg, meta) diff --git a/src/pkgupdates/remote.py b/src/pkgupdates/remote.py index 5af4405..3067031 100644 --- a/src/pkgupdates/remote.py +++ b/src/pkgupdates/remote.py @@ -5,7 +5,9 @@ import re from enum import Enum from xml.dom import minidom import requests +from github import GithubException, Github as GithubApi from .version import rex_semantic +from .settings import get_gh_rss_get class ERemote(Enum): @@ -230,36 +232,25 @@ class Ctan(Remote): NAME = "ctan" -# TODO: get_latest_commit -# TODO: get_latest_release -class FreedesktopGitlab(Remote): - """Remote class for gitlab.freedesktop.org hosted projects - - Gitlab instance of the FreeDesktop project""" - - TYPE = ERemote.FREEDESKTOP_GITLAB - NAME = "freedesktop-gitlab" - - -# TODO: get_latest_commit -# TODO: get_latest_release -class Gentoo(Remote): - """Remote class for gitweb.gentoo.org hosted projects - - Repository for the Gentoo project""" - - TYPE = ERemote.GENTOO - NAME = "gentoo" - - class Github(Remote): """Remote class for github.com hosted projects""" TYPE = ERemote.GITHUB NAME = "github" + API = GithubApi() GITHUB_ID_REX = re.compile("/([0-9a-zA-Z._-]+)$") + @classmethod + def split_user_repo(cls, pkg_repo): + """Split "user/repo" into "username", "reponame" + + Returns two values""" + if isinstance(pkg_repo, str): + elements = pkg_repo.split("/") + return elements[0], elements[1] + return '', '' + @classmethod def get_latest_release(cls, pkg_repo): """Get latest release name from either latest release tag or tag""" @@ -278,16 +269,26 @@ class Github(Remote): Why? Because github requires an API key for API calls. """ - url = "https://github.com/" + pkg_repo + "/releases.atom" - try: - with urllib.request.urlopen(url) as req: - feed = req.read() - except urllib.request.HTTPError: - return "" - dom = minidom.parseString(feed) - entries = dom.getElementsByTagName("entry") - titles = entries[0].getElementsByTagName("title") - version = titles[0].firstChild.nodeValue + if get_gh_rss_get(): + print("Get RSS\n") + url = "https://github.com/" + pkg_repo + "/releases.atom" + try: + with urllib.request.urlopen(url) as req: + feed = req.read() + except urllib.request.HTTPError: + return "" + dom = minidom.parseString(feed) + entries = dom.getElementsByTagName("entry") + titles = entries[0].getElementsByTagName("title") + version = titles[0].firstChild.nodeValue + else: + print("Get API\n") + user, repo = cls.split_user_repo(pkg_repo) + try: + repo = cls.API.get_user(user).get_repo(repo) + version = repo.get_latest_release().title + except GithubException: + return "" return version @@ -299,24 +300,33 @@ class Github(Remote): Why? Because github requires an API key for API calls. """ - url = "https://github.com/" + pkg_repo + "/releases.atom" - try: - with urllib.request.urlopen(url) as req: - feed = req.read() - except urllib.request.HTTPError: - return "" - dom = minidom.parseString(feed) - nodelist = dom.getElementsByTagName("entry") - if len(nodelist) > 0: - nodelist = nodelist[0].getElementsByTagName("id") - else: - return "" - id_str = nodelist[0].firstChild.nodeValue - result = cls.GITHUB_ID_REX.search(id_str) - if result is not None: - version = result.groups()[0] + if get_gh_rss_get(): + url = "https://github.com/" + pkg_repo + "/releases.atom" + try: + with urllib.request.urlopen(url) as req: + feed = req.read() + except urllib.request.HTTPError: + return "" + dom = minidom.parseString(feed) + nodelist = dom.getElementsByTagName("entry") + if len(nodelist) > 0: + nodelist = nodelist[0].getElementsByTagName("id") + else: + return "" + id_str = nodelist[0].firstChild.nodeValue + result = cls.GITHUB_ID_REX.search(id_str) + if result is not None: + version = result.groups()[0] + else: + version = "" + else: - version = "" + user, repo = cls.split_user_repo(pkg_repo) + try: + repo = cls.API.get_user(user).get_repo(repo) + version = repo.get_latest_release().tag_name + except GithubException: + return "" return version @@ -327,15 +337,25 @@ class Github(Remote): Uses the RSS feed. See above why. """ - url = "https://github.com/" + pkg_repo + "/commits.atom" - try: - with urllib.request.urlopen(url) as req: - feed = req.read() - except urllib.request.HTTPError: - return "HTTPError" - dom = minidom.parseString(feed) - nodelist = dom.getElementsByTagName("updated") - version = nodelist[0].firstChild.nodeValue + if get_gh_rss_get(): + url = "https://github.com/" + pkg_repo + "/commits.atom" + try: + with urllib.request.urlopen(url) as req: + feed = req.read() + except urllib.request.HTTPError: + return "HTTPError" + dom = minidom.parseString(feed) + nodelist = dom.getElementsByTagName("updated") + version = nodelist[0].firstChild.nodeValue + + else: + user, repo = cls.split_user_repo(pkg_repo) + try: + repo = cls.API.get_user(user).get_repo(repo) + branch = repo.get_branch(repo.default_branch) + version = branch.commit.commit.timestamp + except GithubException: + return "" return version @@ -346,30 +366,42 @@ class Github(Remote): Uses the RSS feed. See above why. """ - url = "https://github.com/" + pkg_repo + "/tags.atom" - try: - with urllib.request.urlopen(url) as req: - feed = req.read() - except urllib.request.HTTPError: - return "" - dom = minidom.parseString(feed) - nodelist = dom.getElementsByTagName("entry") - if len(nodelist) > 0: - nodelist = nodelist[0].getElementsByTagName("id") - else: - return "" - id_str = nodelist[0].firstChild.nodeValue - result = cls.GITHUB_ID_REX.search(id_str) - if result is not None: - version = result.groups()[0] + if get_gh_rss_get(): + url = "https://github.com/" + pkg_repo + "/tags.atom" + try: + with urllib.request.urlopen(url) as req: + feed = req.read() + except urllib.request.HTTPError: + return "" + dom = minidom.parseString(feed) + nodelist = dom.getElementsByTagName("entry") + if len(nodelist) > 0: + nodelist = nodelist[0].getElementsByTagName("id") + else: + return "" + id_str = nodelist[0].firstChild.nodeValue + result = cls.GITHUB_ID_REX.search(id_str) + if result is not None: + version = result.groups()[0] + else: + version = "" + else: - version = "" + user, repo = cls.split_user_repo(pkg_repo) + try: + repo = cls.API.get_user(user).get_repo(repo) + tags = repo.get_tags() + if tags.totalCount > 0: + version = tags[0].commit.commit.timestamp + else: + return "" + except GithubException: + print(f"{pkg_repo}: API request failed") + return "" return version -# TODO: get_latest_commit -# TODO: get_latest_release class Gitlab(Remote): """Remote class for gitlab.com hosted projects @@ -377,17 +409,76 @@ class Gitlab(Remote): TYPE = ERemote.GITLAB NAME = "gitlab" + URL = "https://gitlab.com/api/v4" + + @classmethod + def get_latest_release(cls, pkg_repo): + ident = pkg_repo.replace("/", "%2F") + url = cls.URL + "/projects/" + ident + "/repository/tags" + version = "" + try: + with requests.get(url, timeout=5) as resp: + data = resp.json() + version = data[0]["name"] + except requests.HTTPError: + return "" + + return version + + @classmethod + def get_latest_commit(cls, pkg_repo): + ident = pkg_repo.replace("/", "%2F") + url = cls.URL + "/projects/" + ident + "/repository/commits" + version = "" + try: + with requests.get(url, timeout=5) as resp: + data = resp.json() + version = data[0]["created_at"] + except requests.HTTPError: + return "" + + return version + + +class FreedesktopGitlab(Gitlab): + """Remote class for gitlab.freedesktop.org hosted projects + + Gitlab instance of the FreeDesktop project""" + + TYPE = ERemote.FREEDESKTOP_GITLAB + NAME = "freedesktop-gitlab" + URL = "https://gitlab.freedesktop.org/api/v4" # TODO: get_latest_commit # TODO: get_latest_release -class GnomeGitlab(Remote): +class Gentoo(Remote): + """Remote class for gitweb.gentoo.org hosted projects + + Repository for the Gentoo project""" + + TYPE = ERemote.GENTOO + NAME = "gentoo" + + +class GnomeGitlab(Gitlab): """Remote class for gitlab.gnome.org hosted projects Gitlab instance of the GNOME project""" TYPE = ERemote.GNOME_GITLAB NAME = "gnome-gitlab" + URL = "https://gitlab.gnome.org/api/v4" + + +class KdeInvent(Gitlab): + """Remote for invent.kde.org hosted projects + + Gitlab instance of the KDE project""" + + TYPE = ERemote.KDE_INVENT + NAME = "kde-invent" + URL = "https://invent.kde.org/api/v4" # TODO: get_latest_commit @@ -421,17 +512,6 @@ class Heptapod(Remote): NAME = "heptapod" -# TODO: get_latest_commit -# TODO: get_latest_release -class KdeInvent(Remote): - """Remote for invent.kde.org hosted projects - - Gitlab instance of the KDE project""" - - TYPE = ERemote.KDE_INVENT - NAME = "kde-invent" - - # TODO: get_latest_commit # TODO: get_latest_release class Launchpad(Remote): @@ -556,6 +636,18 @@ class Sourcehut(Remote): TYPE = ERemote.SOURCEHUT NAME = "sourcehut" + # @classmethod + # def get_latest_commit(cls, pkg_repo): + # url = "https://git.sr.ht/api/" + # body = """ + # query repositoryByDiskPath { + # } + # """ + # try: + # with requests.get(url, json={"query": body}, timeout=5) as resp: + # data = resp.json() + # version = data[0]["created_at"] + # TODO: get_latest_commit # TODO: get_latest_release diff --git a/src/pkgupdates/settings.py b/src/pkgupdates/settings.py new file mode 100644 index 0000000..c0153ac --- /dev/null +++ b/src/pkgupdates/settings.py @@ -0,0 +1,15 @@ +"""Module Settings""" + +GITHUB_RSS_GET = False + + +def get_gh_rss_get(): + """GITHUB_RSS_GET getter""" + return GITHUB_RSS_GET + + +def set_gh_rss_get(v): + """GITHUB_RSS_GET setter""" + global GITHUB_RSS_GET + GITHUB_RSS_GET = v + return GITHUB_RSS_GET