Compare commits

10 Commits

Author SHA1 Message Date
0995cbe960 Add a command to open package websites 2023-07-06 18:21:22 -07:00
b498049703 Make outdated check more verbose 2023-07-06 18:20:42 -07:00
81b9413a2a Remove shebangs
I'm not running these as standalone scripts or anything.
2022-08-05 14:06:10 -07:00
962c214664 Add terrible forge scan 2021-11-04 14:44:19 -07:00
8320df3bc5 List packages maintained by others 2021-09-18 13:52:44 -07:00
51c58c1899 Support some Mercurial hosts
This will need updating from the hardcoded list at some point.
2021-09-17 09:00:28 -07:00
bb152b2112 Fix XDG_CONFIG_HOME check
This allows pkgcrap to fall back to ~/.config instead of crashing.
2021-08-30 01:53:32 +00:00
423ec61cad Remove "release" prefix from tags 2021-08-25 03:26:32 +00:00
5c800652d8 Add tag blacklisting 2021-08-24 22:39:26 +00:00
42d607ba5d Check PyPI packages 2021-08-22 00:21:00 +00:00
8 changed files with 216 additions and 61 deletions

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
if __name__ == "__main__": if __name__ == "__main__":
from pkgcrap.cli import main from pkgcrap.cli import main
main() main()

View File

@@ -1,13 +1,16 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from sys import argv from sys import argv
from pkgcrap.list_update import main as update from pkgcrap.list_update import main as update
from pkgcrap.list_update import maintlist as maintlist
from pkgcrap.outdated_check import main as outdated from pkgcrap.outdated_check import main as outdated
from pkgcrap.forge_scan import main as forgescan
from pkgcrap.website import main as website
options = { options = {
'update': update, 'update': update,
'outdated': outdated, 'outdated': outdated,
'maintlist': maintlist,
'forgescan': forgescan,
'site': website,
} }
def main(): def main():

42
pkgcrap/forge_scan.py Normal file
View File

@@ -0,0 +1,42 @@
from pkgcrap.util import conf_file_path
import pkgcrap.parse as parse
from urllib.parse import urlparse
def main(args):
repos = parse.repos()
repos.load()
checked = 0
failed = 0
forges = {}
for repo in repos.repos.values():
repo.load()
print('Scanning', repo.name)
for cat in repo.categories.values():
cat.load()
for pkg in cat.packages.values():
pkg.load()
if len(pkg.ebuilds) == 0 or 'EGIT_REPO_URI' not in pkg.ebuilds[0].vars:
failed += 1
continue
eb = pkg.ebuilds[0]
repo = pkg.ebuilds[0].vars['EGIT_REPO_URI'].replace('${PN}', pkg.name)
if 'HOMEPAGE' in pkg.ebuilds[0].vars:
repo = repo.replace('${HOMEPAGE}', eb.vars['HOMEPAGE'])
if 'EGO_PN' in pkg.ebuilds[0].vars:
repo = repo.replace('${EGO_PN}', eb.vars['EGO_PN'])
if 'MY_REPO_URI' in pkg.ebuilds[0].vars:
repo = repo.replace('${MY_REPO_URI}', eb.vars['MY_REPO_URI'])
forge = urlparse(repo).netloc
if forge == '' or forge.startswith('${'):
failed += 1
continue
if forge not in forges:
forges[forge] = 0
forges[forge] += 1
checked += 1
print('Found git URI in '+str(checked)+' packages')
print('Failed to find git URI in '+str(failed)+' packages')
for forge in dict(reversed(sorted(forges.items(), key=lambda item: item[1]))):
c = forges[forge]
p = float(c)/checked*100
print(forge+': '+str(c)+f' ({p:.2f}%)')

View File

@@ -1,16 +1,14 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pkgcrap.util import conf_file_path from pkgcrap.util import conf_file_path
import pkgcrap.parse as parse import pkgcrap.parse as parse
def packages_scan(maintainer): def packages_scan(maintainer, verbose=False):
repos = parse.repos() repos = parse.repos()
repos.load() repos.load()
packages = [] packages = []
for repo in repos.repos.values(): for repo in repos.repos.values():
repo.load() repo.load()
print('Scanning', repo.name) if verbose:
print('Scanning', repo.name)
for cat in repo.categories.values(): for cat in repo.categories.values():
cat.load() cat.load()
for pkg in cat.packages.values(): for pkg in cat.packages.values():
@@ -22,11 +20,19 @@ def packages_scan(maintainer):
pass pass
return packages return packages
def maintlist(args):
if len(args) < 1:
print('Specify the maintainer\'s email address')
return
packages = packages_scan(args[0])
for pkg in packages:
print(pkg.full_name)
def main(args): def main(args):
if len(args) < 1: if len(args) < 1:
print('Specify your maintainer email address') print('Specify your maintainer email address')
return return
packages = packages_scan(args[0]) packages = packages_scan(args[0], verbose=True)
with open(conf_file_path('maintained.txt'), 'w') as fp: with open(conf_file_path('maintained.txt'), 'w') as fp:
fp.write("\n".join([pkg.full_name for pkg in packages])) fp.write("\n".join([pkg.full_name for pkg in packages]))
print('Updated maintained package list') print('Updated maintained package list')

View File

@@ -1,37 +1,42 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import urllib.request import urllib.request
import pkgcrap.parse as parse import pkgcrap.parse as parse
from pkgcrap.util import maintained_packages_get import json
from pkgcrap.util import blacklisted_tags_get, maintained_packages_get
from functools import cmp_to_key from functools import cmp_to_key
from libversion import version_compare from libversion import version_compare
def guess_repo(uri): def guess_repo(uri):
forges = [ forges = [
('git', 'https://github.com', True), ('git', 'https://github.com', True),
('https://gitlab.com', True), ('git', 'https://gitlab.com', True),
('https://git.sr.ht', False), ('git', 'https://git.sr.ht', False),
('pypi', 'mirror://pypi', False),
] ]
for forge in forges: for forge in forges:
if uri.startswith(forge[0]): if uri.startswith(forge[1]):
suffix = '.git' if forge[1] == True else '' if forge[0] == 'git':
return '/'.join(uri.split('/')[:5])+suffix suffix = '.git' if forge[2] == True else ''
return (forge[0], '/'.join(uri.split('/')[:5])+suffix)
if forge[0] == 'pypi':
return (forge[0], uri.split('/')[4])
return None return None
def repo_from_metadata(pkg): def repos_from_metadata(pkg):
if len(pkg.metadata.remotes) == 0: remotes = []
return None for remote in pkg.metadata.remotes:
remote = pkg.metadata.remotes[0] if remote[0] == 'github':
if remote[0] == 'github': remotes.append(('git', 'https://github.com/'+remote[1]+'.git'))
return "https://github.com/"+remote[1]+".git" if remote[0] == 'gitlab':
if remote[0] == 'gitlab': remotes.append(('git', 'https://gitlab.com/'+remote[1]+'.git'))
return "https://gitlab.com/"+remote[1]+".git" if remote[0] == 'pypi':
return None remotes.append(('pypi', remote[1]))
return remotes
def repo_from_egit_uri(pkg): def repo_from_repo_uri(pkg):
if 'EGIT_REPO_URI' in pkg.ebuilds[0].vars: if 'EGIT_REPO_URI' in pkg.ebuilds[0].vars:
return pkg.ebuilds[0].vars['EGIT_REPO_URI'].replace('${PN}', pkg.name) return ('git', pkg.ebuilds[0].vars['EGIT_REPO_URI'].replace('${PN}', pkg.name))
if 'EHG_REPO_URI' in pkg.ebuilds[0].vars:
return ('hg', pkg.ebuilds[0].vars['EHG_REPO_URI'].replace('${PN}', pkg.name))
def repo_from_src_uri(pkg): def repo_from_src_uri(pkg):
for eb in pkg.ebuilds: for eb in pkg.ebuilds:
@@ -43,31 +48,38 @@ def repo_from_homepage(pkg):
if 'HOMEPAGE' in eb.vars: if 'HOMEPAGE' in eb.vars:
return guess_repo(eb.vars['HOMEPAGE']) return guess_repo(eb.vars['HOMEPAGE'])
def repo_from_pkg(pkg): def repos_from_pkg(pkg):
repo = repo_from_metadata(pkg) repos = []
if repo is None: repos += repos_from_metadata(pkg)
repo = repo_from_egit_uri(pkg) repos.append(repo_from_repo_uri(pkg))
if repo is None: repos.append(repo_from_src_uri(pkg))
repo = repo_from_src_uri(pkg) repos.append(repo_from_homepage(pkg))
if repo is None: return [repo for repo in repos if repo != None]
repo = repo_from_homepage(pkg)
return repo
def repo_get_latest(repo, pkg): def repo_get_latest_git(uri, pkg, verbose):
global blacklisted_tags
try: try:
r = urllib.request.urlopen(repo+'/info/refs?service=git-upload-pack') r = urllib.request.urlopen(uri+'/info/refs?service=git-upload-pack')
except urllib.error.HTTPError: except urllib.error.HTTPError:
print(pkg.full_name+':', 'Invalid repo!', repo) if verbose:
print(pkg.full_name+':', 'Invalid repo!', uri)
return None return None
tags = [] tags = []
for line in reversed(r.read().decode('utf-8').split("\n")): for line in reversed(r.read().decode('utf-8').split("\n")):
if line[4:44] in blacklisted_tags:
continue
line = line.split(' ')[-1].split("\t")[-1] line = line.split(' ')[-1].split("\t")[-1]
if line[:10] == 'refs/tags/' and line[-3:] != '^{}': if line[:10] == 'refs/tags/' and line[-3:] != '^{}':
tag = line[10:] tag = line[10:]
if tag in blacklisted_tags:
continue
if tag.startswith(pkg.name): if tag.startswith(pkg.name):
tag = tag[len(pkg.name):] tag = tag[len(pkg.name):]
if tag.startswith('version'): version_prefixes = ['version', 'release']
tag = tag[7:] for prefix in version_prefixes:
if tag.startswith(prefix):
tag = tag[len(prefix):]
break
if not tag[0].isdigit() and tag[1].isdigit(): if not tag[0].isdigit() and tag[1].isdigit():
tag = tag[1:] tag = tag[1:]
tags.append(tag) tags.append(tag)
@@ -78,26 +90,73 @@ def repo_get_latest(repo, pkg):
return None return None
return latest return latest
def repo_get_latest_hg(uri, pkg, verbose):
global blacklisted_tags
forges = [
('https://hg.sr.ht/', '/raw/'),
]
raw_path = '/raw-file/tip/'
for forge in forges:
if uri.startswith(forge[0]):
raw_path = forge[1]
try:
r = urllib.request.urlopen(uri+raw_path+'.hgtags')
except urllib.error.HTTPError:
if verbose:
print(pkg.full_name+':', 'Invalid repo!', uri)
return None
for line in r.read().decode('utf-8').split("\n"):
tag = line[41:]
if line[:40] in blacklisted_tags or tag in blacklisted_tags:
continue
latest = tag
return latest
def repo_get_latest_pypi(uri, pkg, verbose):
try:
r = urllib.request.urlopen('https://pypi.org/pypi/'+uri+'/json')
except urllib.error.HTTPError:
if verbose:
print(pkg.full_name+':', 'Invalid PyPI package!', uri)
return None
versions = list(json.load(r)['releases'].keys())
latest = sorted(versions, key=cmp_to_key(version_compare))[-1]
return latest
def repo_get_latest(repo, pkg, verbose):
if repo[0] == 'git':
return repo_get_latest_git(repo[1], pkg, verbose)
if repo[0] == 'hg':
return repo_get_latest_hg(repo[1], pkg, verbose)
if repo[0] == 'pypi':
return repo_get_latest_pypi(repo[1], pkg, verbose)
return None
def outdated_check(pkg, verbose=False): def outdated_check(pkg, verbose=False):
if len(pkg.ebuilds) == 1 and pkg.ebuilds[0].live: if len(pkg.ebuilds) == 1 and pkg.ebuilds[0].live:
if verbose: if verbose:
print(pkg.full_name+':', 'Only has live ebuild') print(pkg.full_name+':', 'Only has live ebuild')
return return
repo = repo_from_pkg(pkg) repos = repos_from_pkg(pkg)
if repo == None: if len(repos) == 0:
if verbose: if verbose:
print(pkg.full_name+':', 'Repo not found') print(pkg.full_name+':', 'Repo not found')
return return
current = pkg.version_latest() current = pkg.version_latest()
latest = repo_get_latest(repo, pkg) for repo in repos:
if verbose:
print(pkg.full_name+':', 'Checking '+repo[0]+' repo ('+repo[1]+')')
latest = repo_get_latest(repo, pkg, verbose)
if latest != None:
break
if latest == None: if latest == None:
if verbose: if verbose:
print(pkg.full_name+':', 'Unable to find latest version!') print(pkg.full_name+':', 'Unable to find latest version!')
return else:
if version_compare(current, latest) == -1: if version_compare(current, latest) == -1:
print(pkg.full_name+':', 'Outdated package!', current, '->', latest) print(pkg.full_name+':', 'Outdated package!', current, '->', latest)
elif verbose: elif verbose:
print(pkg.full_name+':', 'Up to date!', current, '->', latest) print(pkg.full_name+':', 'Up to date!', current, '->', latest)
def main(args): def main(args):
if 'all' in args: if 'all' in args:
@@ -106,6 +165,8 @@ def main(args):
else: else:
packages = [parse.package.from_path('.')] packages = [parse.package.from_path('.')]
verbose = True verbose = True
global blacklisted_tags
blacklisted_tags = blacklisted_tags_get()
for pkg in packages: for pkg in packages:
pkg.load() pkg.load()
outdated_check(pkg, verbose=verbose) outdated_check(pkg, verbose=verbose)

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from os import listdir from os import listdir
from os.path import abspath, isfile, dirname from os.path import abspath, isfile, dirname
from portage.versions import cpv_sort_key from portage.versions import cpv_sort_key

View File

@@ -1,12 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os import os
import pkgcrap.parse as parse import pkgcrap.parse as parse
def conf_dir_path(): def conf_dir_path():
conf_home = '~/.config' conf_home = '~/.config'
if os.environ: if 'XDG_CONFIG_HOME' in os.environ:
conf_home = os.environ['XDG_CONFIG_HOME'] conf_home = os.environ['XDG_CONFIG_HOME']
if conf_home[0] == '~': if conf_home[0] == '~':
conf_home = os.environ['HOME']+'/'+conf_home[1:] conf_home = os.environ['HOME']+'/'+conf_home[1:]
@@ -34,3 +31,12 @@ def maintained_packages_get():
cat.load() cat.load()
packages.append(cat.packages[pkg_info[1]]) packages.append(cat.packages[pkg_info[1]])
return packages return packages
def blacklisted_tags_get():
blacklisted_tags = []
with open(conf_file_path('eviltags.txt'), 'r') as fp:
for line in fp.read().split('\n'):
if len(line) > 0 and line[0] == '#':
continue
blacklisted_tags.append(line.split(' ',1)[0])
return blacklisted_tags

43
pkgcrap/website.py Normal file
View File

@@ -0,0 +1,43 @@
import webbrowser
import pkgcrap.parse as parse
def repo2site(repo):
return repo[:-4] if repo.endswith('.git') else repo
def stripslash(url):
return url[:-1] if url.endswith('/') else url
def main(args):
pkg = parse.package.from_path('.')
pkg.load()
sites = list()
if len(pkg.ebuilds) > 0:
if 'HOMEPAGE' in pkg.ebuilds[0].vars:
sites.append(stripslash(pkg.ebuilds[0].vars['HOMEPAGE']))
if 'EGIT_REPO_URI' in pkg.ebuilds[0].vars:
sites.append(repo2site(pkg.ebuilds[0].vars['EGIT_REPO_URI']))
for remote in pkg.metadata.remotes:
if remote[0] == 'github':
sites.append('https://github.com/'+remote[1])
if remote[0] == 'gitlab':
sites.append('https://gitlab.com/'+remote[1])
if remote[0] == 'pypi':
sites.append('https://pypi.org/project/'+remote[1]+'/')
sites = sorted(list(set(sites)))
match len(sites):
case 0:
print('No sites found :-(')
exit(1)
case 1:
print('Opening '+sites[0])
webbrowser.open(sites[0])
case _:
print('Sites:')
for i in range(len(sites)):
print(str(i+1)+') '+sites[i])
pick = input('Pick one: ')
pick = sites[int(pick)-1]
print('Opening '+pick)
webbrowser.open(pick)