almost working impl

This commit is contained in:
setop 2023-02-26 23:25:26 +01:00
parent dd9396cfdf
commit 89aa834bc7
2 changed files with 68 additions and 21 deletions

View File

@ -36,6 +36,7 @@
- [ ] existence of `.local`, `.local/programs`, `.local/bin`
- [ ] that permissions are ok
- [ ] that `.local/bin` are in `PATH`
- [ ] UI, add some progress to install
- [ ] allow to list versions
- [ ] allow to force installation of a given version
- [-] improve search algorithm, fuzzing

View File

@ -1,15 +1,17 @@
"""Cliget - install cli tools in you user profile
Usage:
cliget [-v] [-c URL] list
cliget [-v] [-c URL] search PAT
cliget [-v] [-c URL] info TOOL
cliget [-v] [-c URL] list
cliget [-v] [-c URL] versions TOOLS ...
cliget [-v] [-c URL] allversions TOOL
cliget [-v] [-c URL] install TOOLS ...
cliget [-v] [-c URL] update [--all] [TOOLS ...]
list list all installed tools
search search catalog for tools with the given pattern
info show details of an entry in the catalog
list list all installed tools
versions TOOLS list current and latest versions of some tools
allversions TOOL list all versions of a tool
install TOOLS install some tools
@ -25,7 +27,7 @@ Options:
"""
import logging
import sys, os
import sys, os, shutil
from docopt import docopt
from yaml import load, SafeLoader
from semver import VersionInfo
@ -39,7 +41,7 @@ def trace(*mess):
print("TRACE", mess)
def warn(*mess):
print(f"WARN {mess[0]}")
print("WARN", mess)
class DotDict(dict):
def __getattr__(self, name):
@ -61,12 +63,12 @@ def _find_semver(s:str) -> VersionInfo:
except Exception as e:
trace("parse error", e)
return ver
def _local_version(cmd):
ver = VersionInfo(0)
try:
first_line = run([cmd, '--version'], input='', text=True, capture_output=True, check=True, timeout=0.1).stdout.split('\n')[0]
trace(cmd, '=>', first_line)
ver = _find_semver(first_line)
except Exception as e:
trace("run error", e)
@ -92,6 +94,16 @@ def dolist(options):
for (cli, _, ver) in _internal_list(options):
print(cli, ver)
def doinfo(options):
tool = options.TOOL
ctl = _load_catalog(options)
if tool in ctl:
print(tool)
for (k,v) in ctl[tool].items():
print(f' {k}: {v}')
else:
warn(f'{tool} not in catalog')
def dosearch(options):
pat = options.PAT
ctl = _load_catalog(options)
@ -100,7 +112,7 @@ def dosearch(options):
trace(cli, props)
rtitle = fuzz.ratio(cli, pat)
rdesc = fuzz.partial_token_set_ratio(props['desc'], pat)
score = 0.2 * rtitle + rdesc
score = rtitle + rdesc if rtitle>60 else rdesc
L.append((cli, props['desc'], score))
L = sorted(L, key=lambda x: -x[-1])
# TODO format a as table
@ -114,7 +126,6 @@ def dolistupdate(options):
pass
pass
def _gh_versions(repo:str) -> [VersionInfo|None]:
[owner, repo] = repo.split("/")
url = f'https://api.github.com/repos/{owner}/{repo}/releases'
@ -140,7 +151,7 @@ def doversions(options):
print(f"{cli} | {lver} | {rver}")
else:
warn(f'{tool} not in catalog')
def doallversions(options):
tool = options.TOOL
ctl = _load_catalog(options)
@ -152,7 +163,6 @@ def doallversions(options):
print("\n".join(vers))
else:
warn(f'{tool} not in catalog')
def doinstall(options):
tools = options.TOOLS
@ -161,19 +171,53 @@ def doinstall(options):
if tool in ctl:
props = ctl[tool]
if props.github:
vers = _gh_version(props.github)
trace(vers)
rver = _gh_version(props.github)
lver = _local_version(tool)
trace(lver,rver)
if rver > lver:
_perform_install(tool, props.github)
else:
warn(f'{tool} not in catalog')
def perform_install(cli, repo):
# get arch and os
# dl asset
# mkdirs
# unpack
# symlink
def _match_arch_machine(name:str) -> bool:
sysname = os.uname().sysname.lower() # os
machine = os.uname().machine.lower() # arch
return name.lower().find(sysname)>0 and name.lower().find(machine)>0
def _perform_install(cli, repo, version=None):
# get asset list
[owner, repo] = repo.split("/")
url = f'https://api.github.com/repos/{owner}/{repo}/releases/latest'
r = requests.get(url)
assets = r.json()['assets']
trace(assets)
# select right asset
asset = DotDict(next(filter(lambda x: _match_arch_machine(x['name']),assets)))
trace(asset.name, asset.url)
# mkdirs
p = os.path
home = os.environ["HOME"]
assetshome = p.join(home, '.cache/cliget/assets')
os.makedirs(assetshome, exist_ok=True)
location = p.join(assetshome, asset.name)
trace(f"will dl {location}")
# dl asset if not already there
if not p.exists(location):
dlurl = requests.get(asset.url).json()['browser_download_url']
r = requests.get(dlurl, allow_redirects=True, stream=True)
with open(location, 'wb') as fd:
shutil.copyfileobj(r.raw, fd)
trace("downloaded")
# unpack asset
if not asset.name.endswith('.tar.gz'):
raise ValueError('package type not handled')
progloc = p.join(home, '.local/program/'+cli)
trace("process tgz")
os.makedirs(progloc, exist_ok=True)
run(["tar", "xfz", location, "-C", progloc])
# symlink
os.symlink(p.join(progloc, cli), p.join(home, '.local/bin', cli))
if __name__ == '__main__':
if "DEBUG" in os.environ:
logging.basicConfig(level=logging.DEBUG)
@ -183,13 +227,15 @@ if __name__ == '__main__':
options = docopt(__doc__, version='Cliget 0.1.0')
options = DotDict({k.replace('-','_'):v for (k,v) in options.items() if v is not None})
trace(options)
if options.list:
if options.info:
doinfo(options)
elif options.list:
dolist(options)
elif options.search:
dosearch(options)
elif options.versions:
doversions(options)
elif options.allversions(options):
elif options.allversions:
doallversions(options)
elif options.install or options.update:
if not options.__all and len(options.TOOLS)==0: