aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur Zamarin <arthurzam@gentoo.org>2022-10-28 22:25:34 +0300
committerArthur Zamarin <arthurzam@gentoo.org>2022-10-29 18:10:59 +0300
commitc1f105c44cd43f073d892ea06c371b6e0a6fe28d (patch)
tree641f44bb793963cdd36e3e4e8cd3d1a1ccdd2fa6
parentBetterCompressionCheck: fix false positive when fixed (diff)
downloadpkgcheck-c1f105c44cd43f073d892ea06c371b6e0a6fe28d.tar.gz
pkgcheck-c1f105c44cd43f073d892ea06c371b6e0a6fe28d.tar.bz2
pkgcheck-c1f105c44cd43f073d892ea06c371b6e0a6fe28d.zip
MissingRemoteIdCheck: check for missing remote-id
Scans HOMEPAGE and SRC_URI for uris matching regexes using which it extracts remote-id. Skips already defined remote-id types. Skips URIS that end with ".diff" or ".patch". Prefers to take remote-id from newer package versions, in case URL updated. Resolves: https://github.com/pkgcore/pkgcheck/issues/475 Signed-off-by: Arthur Zamarin <arthurzam@gentoo.org>
-rw-r--r--src/pkgcheck/checks/metadata_xml.py78
-rw-r--r--testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/expected.json4
-rw-r--r--testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/fix.patch13
-rw-r--r--testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-0.ebuild2
-rw-r--r--testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-1.ebuild2
-rw-r--r--testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/Manifest8
-rw-r--r--testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-0.ebuild16
-rw-r--r--testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-1.ebuild10
-rw-r--r--testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-2.ebuild7
-rw-r--r--testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-3.ebuild9
-rw-r--r--testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-4.ebuild11
-rw-r--r--testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/metadata.xml10
-rw-r--r--testdata/repos/eapis-testing/profiles/thirdpartymirrors2
-rw-r--r--tests/scripts/test_pkgcheck_scan.py4
14 files changed, 172 insertions, 4 deletions
diff --git a/src/pkgcheck/checks/metadata_xml.py b/src/pkgcheck/checks/metadata_xml.py
index 67f0f42c..56dd2c94 100644
--- a/src/pkgcheck/checks/metadata_xml.py
+++ b/src/pkgcheck/checks/metadata_xml.py
@@ -1,11 +1,15 @@
import os
import re
from difflib import SequenceMatcher
+from itertools import chain
from lxml import etree
from pkgcore import const as pkgcore_const
from pkgcore.ebuild.atom import MalformedAtom, atom
+from pkgcore.restrictions.packages import Conditional
+from pkgcore.fetch import fetchable
from snakeoil.osutils import pjoin
+from snakeoil.sequences import iflatten_instance
from snakeoil.strings import pluralism
from .. import results, sources
@@ -553,3 +557,77 @@ class CategoryMetadataXmlCheck(_XmlBaseCheck):
def _get_xml_location(self, pkg):
"""Return the metadata.xml location for a given package's category."""
return pjoin(self.repo_base, pkg.category, 'metadata.xml')
+
+
+class MissingRemoteId(results.PackageResult, results.Info):
+ """Missing remote-id which was inferred from ebuilds.
+
+ Based on URIs found in SRC_URI and HOMEPAGE, a remote-id can be suggested.
+ If a remote-id of same type is already defined in ``metadata.xml``, the
+ suggestion won't be reported. It ignores URIs ending with ``.diff`` or
+ ``.patch``, as they might point to a fork or developer's space. It also
+ ignores URIs that are conditional on USE flags.
+ """
+
+ def __init__(self, remote_type: str, value: str, uri: str, **kwarg):
+ super().__init__(**kwarg)
+ self.remote_type = remote_type
+ self.value = value
+ self.uri = uri
+
+ @property
+ def desc(self):
+ return (f'missing remote-id of type {self.remote_type!r} with '
+ f'value {self.value!r} (inferred from URI {self.uri!r})')
+
+
+class MissingRemoteIdCheck(Check):
+ """Detect missing remote-ids based on SRC_URI and HOMEPAGE."""
+
+ _source = sources.PackageRepoSource
+ known_results = frozenset([MissingRemoteId])
+
+ _gitlab_match = r'(?P<value>(\w[^/]*/)*\w[^/]*/\w[^/]*)'
+
+ remotes_map = (
+ ('bitbucket', r'https://bitbucket.org/(?P<value>[^/]+/[^/]+)'),
+ ('freedesktop-gitlab', rf'https://gitlab.freedesktop.org/{_gitlab_match}'),
+ ('github', r'https://github.com/(?P<value>[^/]+/[^/]+)'),
+ ('gitlab', rf'https://gitlab.com/{_gitlab_match}'),
+ ('gnome-gitlab', rf'https://gitlab.gnome.org/{_gitlab_match}'),
+ ('heptapod', rf'https://foss.heptapod.net/{_gitlab_match}'),
+ ('launchpad', r'https://launchpad.net/(?P<value>[^/]+)'),
+ ('pypi', r'https://pypi.org/project/(?P<value>[^/]+)'),
+ ('pypi', r'https://files.pythonhosted.org/packages/source/\S/(?P<value>[^/]+)'),
+ ('savannah', r'https://savannah.gnu.org/projects/(?P<value>[^/]+)'),
+ ('savannah-nongnu', r'https://savannah.nongnu.org/projects/(?P<value>[^/]+)'),
+ ('sourceforge', r'https://(?P<value>[^/]+).sourceforge.net/'),
+ ('sourceforge', r'https://sourceforge.net/projects/(?P<value>[^/]+)'),
+ ('sourceforge', r'https://downloads.sourceforge.net/(?:project/)?(?P<value>[^/]+)'),
+ ('sourcehut', r'https://sr.ht/(?P<value>[^/]+/[^/]+)'),
+ )
+
+ def __init__(self, options, **kwargs):
+ super().__init__(options, **kwargs)
+ self.remotes_map = tuple((remote_type, re.compile(regex)) for remote_type, regex in self.remotes_map)
+
+ def feed(self, pkgset):
+ remotes = {u.type: (None, None) for u in pkgset[0].upstreams}
+ for pkg in sorted(pkgset, reverse=True):
+ fetchables = iflatten_instance(pkg.generate_fetchables(allow_missing_checksums=True,
+ ignore_unknown_mirrors=True, skip_default_mirrors=True), (fetchable, Conditional))
+ all_urls = set(chain.from_iterable(f.uri for f in fetchables if isinstance(f, fetchable)))
+ urls = {url for url in all_urls if not url.endswith(('.patch', '.diff'))}
+ urls = sorted(urls.union(pkg.homepage), key=len)
+
+ for remote_type, regex in self.remotes_map:
+ if remote_type in remotes:
+ continue
+ for url in urls:
+ if mo := regex.match(url):
+ remotes[remote_type] = (mo.group('value'), url)
+ break
+
+ for remote_type, (value, url) in remotes.items():
+ if value is not None:
+ yield MissingRemoteId(remote_type, value, url, pkg=pkgset[0])
diff --git a/testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/expected.json b/testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/expected.json
new file mode 100644
index 00000000..ab5ae2d4
--- /dev/null
+++ b/testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/expected.json
@@ -0,0 +1,4 @@
+{"__class__": "MissingRemoteId", "category": "MissingRemoteIdCheck", "package": "MissingRemoteId", "remote_type": "gitlab", "value": "pkgcore/pkgcheck/extra/MissingRemoteId", "uri": "https://gitlab.com/pkgcore/pkgcheck/extra/MissingRemoteId/-/archive/1/MissingRemoteId-1.tar.bz2"}
+{"__class__": "MissingRemoteId", "category": "MissingRemoteIdCheck", "package": "MissingRemoteId", "remote_type": "heptapod", "value": "pkgcore/pkgcore", "uri": "https://foss.heptapod.net/pkgcore/pkgcore/-/archive/4/MissingRemoteId-4.tar.bz2"}
+{"__class__": "MissingRemoteId", "category": "MissingRemoteIdCheck", "package": "MissingRemoteId", "remote_type": "pypi", "value": "MissingRemoteId", "uri": "https://files.pythonhosted.org/packages/source/M/MissingRemoteId/MissingRemoteId-1.tar.gz"}
+{"__class__": "MissingRemoteId", "category": "MissingRemoteIdCheck", "package": "MissingRemoteId", "remote_type": "sourceforge", "value": "pkgcheck", "uri": "https://pkgcheck.sourceforge.net/"}
diff --git a/testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/fix.patch b/testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/fix.patch
new file mode 100644
index 00000000..035f9ad4
--- /dev/null
+++ b/testdata/data/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/fix.patch
@@ -0,0 +1,13 @@
+--- eapis-testing/MissingRemoteIdCheck/MissingRemoteId/metadata.xml
++++ fixed/MissingRemoteIdCheck/MissingRemoteId/metadata.xml
+@@ -3,6 +3,10 @@
+ <pkgmetadata>
+ <upstream>
+ <remote-id type="bitbucket">pkgcore/pkgcheck</remote-id>
++ <remote-id type="gitlab">pkgcore/pkgcheck/extra/MissingRemoteId</remote-id>
++ <remote-id type="heptapod">pkgcore/pkgcheck</remote-id>
++ <remote-id type="pypi">MissingRemoteId</remote-id>
++ <remote-id type="sourceforge">pkgcheck</remote-id>
+ </upstream>
+ <use>
+ <flag name="test">enable tests</flag>
diff --git a/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-0.ebuild b/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-0.ebuild
index cd015c21..dddf98ef 100644
--- a/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-0.ebuild
+++ b/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-0.ebuild
@@ -1,7 +1,7 @@
EAPI=7
DESCRIPTION="Ebuild with stable keywords on EAPI marked as stable"
-HOMEPAGE="https://github.com/pkgcore/pkgcheck"
+HOMEPAGE="https://pkgcore.github.io/pkgcheck/"
SRC_URI=""
LICENSE="BSD"
diff --git a/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-1.ebuild b/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-1.ebuild
index 5ae1a8d9..40751969 100644
--- a/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-1.ebuild
+++ b/testdata/repos/eapis-testing/EapiCheck/StableKeywordsOnTestingEapi/StableKeywordsOnTestingEapi-1.ebuild
@@ -1,7 +1,7 @@
EAPI=8
DESCRIPTION="Ebuild with stable keywords on EAPI marked as testing"
-HOMEPAGE="https://github.com/pkgcore/pkgcheck"
+HOMEPAGE="https://pkgcore.github.io/pkgcheck/"
SRC_URI=""
LICENSE="BSD"
diff --git a/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/Manifest b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/Manifest
new file mode 100644
index 00000000..112fbef4
--- /dev/null
+++ b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/Manifest
@@ -0,0 +1,8 @@
+DIST MissingRemoteId-0.gh.tar.gz 474194 BLAKE2B 8abccad85b39a1c3125ba6685f568892e395316138460b7c6d09ce781f4565f66e812a730e1ab5ae7c9bf83bd3bb9e088623036ae12a9f17f54f8fc31b019ecd SHA512 be433a54fa5cf4c421e74af0d8556ce4f666089260aaecc280f53e4cafbdea2b97735e25d22af0c44928b0cba57ce19aaa7d3a25e23ac0d87624e596ddb27e34
+DIST MissingRemoteId-1-extra.tar.bz2 471942 BLAKE2B 534a32f107067f4237da2cc396d65f42d81f6b8b3bf09abbb84ffd6a33542374d66a15047bb3428c12e4debc771a5661ba3db42c8b7009914815d6c137673b4b SHA512 078310c9cc0154f1ffec6c6c25b3d301a644cc2163cc1dadb24a5f77a4c3d89583dfc7f44972078d59393cdac028bfeb3b481ab03fe9f5a3ebfd6bb0f5ba3a73
+DIST MissingRemoteId-1.tar.gz 497163 BLAKE2B 600b51c5c034356fcfbbbb741681e7e304ccf14b9390207305d35c48dff7675e808101fa76f6a4ce250691b2fd99deae7d59cc91560d609c1a2c3d7421859849 SHA512 3539d877b63e739877f79340ff59c3592f43ac9e00b507ab63d7afc2bb3eaa32f863e7cc8029c5a05bcf7068de70fd3149447054f0d9d304d4e3548a0a25d676
+DIST MissingRemoteId-2.tar.gz 467493 BLAKE2B ecfc1e435397f52cc174724b08e806ee92b49438d05f28fb9a698d2a5778b8fae3a5aa8cfa574512725d34a364774722ddb38689d7db13cd2fcfccc7451f69f1 SHA512 15da0fb2c3615739a8ebd268e1b7a924e24c43f1c7390a78793b8345480a7a23718f281616dbabc9510ac002005c27ec3c64743368593b9413198ae0718395ff
+DIST MissingRemoteId-3.tar.bz2 469597 BLAKE2B 6f2234da65c9903dd1c7727e163f05965ebdbb303cfab162bc0070a79507981e2b88917e0d3eaab20d352c6f369d71acba195a8cc0c5f6ae28252b1e369658c7 SHA512 a40641bf6dba77b6b98e20d95ff6f92a48a500f737e55ef1d500c4469bb8eb489af83c84264731dac0654caadbd520105ef85d669fe01930f507764750f44f35
+DIST MissingRemoteId-4.tar.bz2 467403 BLAKE2B e31bdf219d4aa01c2255f1a6b29e88457b9ea7bb078c5eb797e100662e0635b6eb28d15c411aaa3e676fd0a8fb929f9e50018bbcaa1856999469e2b06ad2b43c SHA512 b5d3978acce3b03f2b46fc2e7bf5e61185bfb745835a375d7d1d76bb210ef7f661e0a938e1510a1f9b815d9fe832f318f2f23d23040a21782a2502a473c561fd
+DIST MissingRemoteId-486.diff 4480 BLAKE2B 9fd62254dd8a4f0cbbaaf7edde1eea013e6b06cacf90b605cda8fea8afac678a13450854a5a8b0a37e730675e31229edb2be1d30756e87ee0de3af5334ba74b5 SHA512 ba45d0eb7bb47eaddf950ff535e1cf448bfcf5528615efc214c71c8489a0578871763e6909327f2d86784905cfe78d15441991615c59c97e887c3a71fce80094
+DIST MissingRemoteId-486.patch 14111416 BLAKE2B fd3ac79ffe3ca031d40a6523a4ae900200c1b1eea0369fd1eec3233f852af8edffd81ca6c0bba628132221346d283d2138794e1f9697074fc1044944ad852e5b SHA512 a2cce0b1fd2267cc8dbd3e2ad3ec1f99526de3988443c0937405cc7b2eab9ef517001ef0b4b387e8721d3c33610cd642c6b7cf83217996a83481830de9713de7
diff --git a/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-0.ebuild b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-0.ebuild
new file mode 100644
index 00000000..5b605897
--- /dev/null
+++ b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-0.ebuild
@@ -0,0 +1,16 @@
+EAPI=7
+
+DESCRIPTION="Skip patch and diff urls, and urls behind use flags"
+HOMEPAGE="https://pkgcore.github.io/pkgcheck/"
+SRC_URI="
+ https://github.com/pkgcore/pkgcheck/pull/486.patch -> ${PN}-486.patch
+ https://github.com/pkgcore/pkgcheck/pull/486.diff -> ${PN}-486.diff
+ test? (
+ https://github.com/pkgcore/pkgcheck/archive/v${PV}.tar.gz
+ -> ${P}.gh.tar.gz
+ )
+"
+LICENSE="BSD"
+SLOT="0"
+IUSE="test"
+RESTRICT="!test? ( test )"
diff --git a/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-1.ebuild b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-1.ebuild
new file mode 100644
index 00000000..2a49ad82
--- /dev/null
+++ b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-1.ebuild
@@ -0,0 +1,10 @@
+EAPI=7
+
+DESCRIPTION="Use from mirror://pypi and gitlab archive"
+HOMEPAGE="https://pkgcore.github.io/pkgcheck/"
+SRC_URI="
+ mirror://pypi/${PN::1}/${PN}/${P}.tar.gz
+ https://gitlab.com/pkgcore/pkgcheck/extra/${PN}/-/archive/${PV}/${P}.tar.bz2 -> ${P}-extra.tar.bz2
+"
+LICENSE="BSD"
+SLOT="0"
diff --git a/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-2.ebuild b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-2.ebuild
new file mode 100644
index 00000000..ed226b8e
--- /dev/null
+++ b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-2.ebuild
@@ -0,0 +1,7 @@
+EAPI=7
+
+DESCRIPTION="Check homepage"
+HOMEPAGE="https://pkgcheck.sourceforge.net/"
+SRC_URI="mirror://sourceforge/${PN}/${P}.tar.gz"
+LICENSE="BSD"
+SLOT="0"
diff --git a/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-3.ebuild b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-3.ebuild
new file mode 100644
index 00000000..e9a20138
--- /dev/null
+++ b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-3.ebuild
@@ -0,0 +1,9 @@
+EAPI=7
+
+DESCRIPTION="Don't suggest where already value exists"
+HOMEPAGE="https://bitbucket.org/pkgcore/pkgcore"
+SRC_URI="
+ https://bitbucket.org/pkgcore/pkgdev/get/v${PV}.tar.bz2 -> ${P}.tar.bz2
+"
+LICENSE="BSD"
+SLOT="0"
diff --git a/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-4.ebuild b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-4.ebuild
new file mode 100644
index 00000000..970d959e
--- /dev/null
+++ b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/MissingRemoteId-4.ebuild
@@ -0,0 +1,11 @@
+EAPI=7
+
+DESCRIPTION="Don't suggest where already value exists"
+HOMEPAGE="https://pkgcore.github.io/pkgcheck/"
+SRC_URI="
+ https://foss.heptapod.net/pkgcore/pkgcheck/-/archive/${PV}/${P}.tar.bz2
+ https://foss.heptapod.net/pkgcore/pkgcheck/extra/-/archive/${PV}/${P}.tar.bz2
+ https://foss.heptapod.net/pkgcore/pkgcore/-/archive/${PV}/${P}.tar.bz2
+"
+LICENSE="BSD"
+SLOT="0"
diff --git a/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/metadata.xml b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/metadata.xml
new file mode 100644
index 00000000..2048587f
--- /dev/null
+++ b/testdata/repos/eapis-testing/MissingRemoteIdCheck/MissingRemoteId/metadata.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
+<pkgmetadata>
+ <upstream>
+ <remote-id type="bitbucket">pkgcore/pkgcheck</remote-id>
+ </upstream>
+ <use>
+ <flag name="test">enable tests</flag>
+ </use>
+</pkgmetadata>
diff --git a/testdata/repos/eapis-testing/profiles/thirdpartymirrors b/testdata/repos/eapis-testing/profiles/thirdpartymirrors
new file mode 100644
index 00000000..15399f5c
--- /dev/null
+++ b/testdata/repos/eapis-testing/profiles/thirdpartymirrors
@@ -0,0 +1,2 @@
+pypi https://files.pythonhosted.org/packages/source
+sourceforge https://downloads.sourceforge.net
diff --git a/tests/scripts/test_pkgcheck_scan.py b/tests/scripts/test_pkgcheck_scan.py
index 8caed9f2..8b8113e7 100644
--- a/tests/scripts/test_pkgcheck_scan.py
+++ b/tests/scripts/test_pkgcheck_scan.py
@@ -406,9 +406,9 @@ class TestPkgcheckScan:
# create stub profile to suppress ArchesWithoutProfiles result
repo.create_profiles([Profile('stub', 'amd64')])
# create ebuild with unknown keywords
- repo.create_ebuild('cat/pkg-0', keywords=['unknown'])
+ repo.create_ebuild('cat/pkg-0', keywords=['unknown'], homepage='https://example.com')
# and a good ebuild for the latest version
- repo.create_ebuild('cat/pkg-1', keywords=['amd64'])
+ repo.create_ebuild('cat/pkg-1', keywords=['amd64'], homepage='https://example.com')
# results for old pkgs will be shown by default
args = ['-r', repo.location]