almost working impl
This commit is contained in:
parent
dd9396cfdf
commit
89aa834bc7
1
TODO.md
1
TODO.md
|
@ -36,6 +36,7 @@
|
||||||
- [ ] existence of `.local`, `.local/programs`, `.local/bin`
|
- [ ] existence of `.local`, `.local/programs`, `.local/bin`
|
||||||
- [ ] that permissions are ok
|
- [ ] that permissions are ok
|
||||||
- [ ] that `.local/bin` are in `PATH`
|
- [ ] that `.local/bin` are in `PATH`
|
||||||
|
- [ ] UI, add some progress to install
|
||||||
- [ ] allow to list versions
|
- [ ] allow to list versions
|
||||||
- [ ] allow to force installation of a given version
|
- [ ] allow to force installation of a given version
|
||||||
- [-] improve search algorithm, fuzzing
|
- [-] improve search algorithm, fuzzing
|
||||||
|
|
82
cliget.py
82
cliget.py
|
@ -1,15 +1,17 @@
|
||||||
"""Cliget - install cli tools in you user profile
|
"""Cliget - install cli tools in you user profile
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
cliget [-v] [-c URL] list
|
|
||||||
cliget [-v] [-c URL] search PAT
|
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] versions TOOLS ...
|
||||||
cliget [-v] [-c URL] allversions TOOL
|
cliget [-v] [-c URL] allversions TOOL
|
||||||
cliget [-v] [-c URL] install TOOLS ...
|
cliget [-v] [-c URL] install TOOLS ...
|
||||||
cliget [-v] [-c URL] update [--all] [TOOLS ...]
|
cliget [-v] [-c URL] update [--all] [TOOLS ...]
|
||||||
|
|
||||||
list list all installed tools
|
|
||||||
search search catalog for tools with the given pattern
|
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
|
versions TOOLS list current and latest versions of some tools
|
||||||
allversions TOOL list all versions of a tool
|
allversions TOOL list all versions of a tool
|
||||||
install TOOLS install some tools
|
install TOOLS install some tools
|
||||||
|
@ -25,7 +27,7 @@ Options:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys, os
|
import sys, os, shutil
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
from yaml import load, SafeLoader
|
from yaml import load, SafeLoader
|
||||||
from semver import VersionInfo
|
from semver import VersionInfo
|
||||||
|
@ -39,7 +41,7 @@ def trace(*mess):
|
||||||
print("TRACE", mess)
|
print("TRACE", mess)
|
||||||
|
|
||||||
def warn(*mess):
|
def warn(*mess):
|
||||||
print(f"WARN {mess[0]}")
|
print("WARN", mess)
|
||||||
|
|
||||||
class DotDict(dict):
|
class DotDict(dict):
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
|
@ -62,11 +64,11 @@ def _find_semver(s:str) -> VersionInfo:
|
||||||
trace("parse error", e)
|
trace("parse error", e)
|
||||||
return ver
|
return ver
|
||||||
|
|
||||||
|
|
||||||
def _local_version(cmd):
|
def _local_version(cmd):
|
||||||
ver = VersionInfo(0)
|
ver = VersionInfo(0)
|
||||||
try:
|
try:
|
||||||
first_line = run([cmd, '--version'], input='', text=True, capture_output=True, check=True, timeout=0.1).stdout.split('\n')[0]
|
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)
|
ver = _find_semver(first_line)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
trace("run error", e)
|
trace("run error", e)
|
||||||
|
@ -92,6 +94,16 @@ def dolist(options):
|
||||||
for (cli, _, ver) in _internal_list(options):
|
for (cli, _, ver) in _internal_list(options):
|
||||||
print(cli, ver)
|
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):
|
def dosearch(options):
|
||||||
pat = options.PAT
|
pat = options.PAT
|
||||||
ctl = _load_catalog(options)
|
ctl = _load_catalog(options)
|
||||||
|
@ -100,7 +112,7 @@ def dosearch(options):
|
||||||
trace(cli, props)
|
trace(cli, props)
|
||||||
rtitle = fuzz.ratio(cli, pat)
|
rtitle = fuzz.ratio(cli, pat)
|
||||||
rdesc = fuzz.partial_token_set_ratio(props['desc'], 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.append((cli, props['desc'], score))
|
||||||
L = sorted(L, key=lambda x: -x[-1])
|
L = sorted(L, key=lambda x: -x[-1])
|
||||||
# TODO format a as table
|
# TODO format a as table
|
||||||
|
@ -114,7 +126,6 @@ def dolistupdate(options):
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _gh_versions(repo:str) -> [VersionInfo|None]:
|
def _gh_versions(repo:str) -> [VersionInfo|None]:
|
||||||
[owner, repo] = repo.split("/")
|
[owner, repo] = repo.split("/")
|
||||||
url = f'https://api.github.com/repos/{owner}/{repo}/releases'
|
url = f'https://api.github.com/repos/{owner}/{repo}/releases'
|
||||||
|
@ -153,7 +164,6 @@ def doallversions(options):
|
||||||
else:
|
else:
|
||||||
warn(f'{tool} not in catalog')
|
warn(f'{tool} not in catalog')
|
||||||
|
|
||||||
|
|
||||||
def doinstall(options):
|
def doinstall(options):
|
||||||
tools = options.TOOLS
|
tools = options.TOOLS
|
||||||
ctl = _load_catalog(options)
|
ctl = _load_catalog(options)
|
||||||
|
@ -161,18 +171,52 @@ def doinstall(options):
|
||||||
if tool in ctl:
|
if tool in ctl:
|
||||||
props = ctl[tool]
|
props = ctl[tool]
|
||||||
if props.github:
|
if props.github:
|
||||||
vers = _gh_version(props.github)
|
rver = _gh_version(props.github)
|
||||||
trace(vers)
|
lver = _local_version(tool)
|
||||||
|
trace(lver,rver)
|
||||||
|
if rver > lver:
|
||||||
|
_perform_install(tool, props.github)
|
||||||
else:
|
else:
|
||||||
warn(f'{tool} not in catalog')
|
warn(f'{tool} not in catalog')
|
||||||
|
|
||||||
def perform_install(cli, repo):
|
def _match_arch_machine(name:str) -> bool:
|
||||||
# get arch and os
|
sysname = os.uname().sysname.lower() # os
|
||||||
# dl asset
|
machine = os.uname().machine.lower() # arch
|
||||||
# mkdirs
|
return name.lower().find(sysname)>0 and name.lower().find(machine)>0
|
||||||
# unpack
|
|
||||||
# symlink
|
|
||||||
|
|
||||||
|
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 __name__ == '__main__':
|
||||||
if "DEBUG" in os.environ:
|
if "DEBUG" in os.environ:
|
||||||
|
@ -183,13 +227,15 @@ if __name__ == '__main__':
|
||||||
options = docopt(__doc__, version='Cliget 0.1.0')
|
options = docopt(__doc__, version='Cliget 0.1.0')
|
||||||
options = DotDict({k.replace('-','_'):v for (k,v) in options.items() if v is not None})
|
options = DotDict({k.replace('-','_'):v for (k,v) in options.items() if v is not None})
|
||||||
trace(options)
|
trace(options)
|
||||||
if options.list:
|
if options.info:
|
||||||
|
doinfo(options)
|
||||||
|
elif options.list:
|
||||||
dolist(options)
|
dolist(options)
|
||||||
elif options.search:
|
elif options.search:
|
||||||
dosearch(options)
|
dosearch(options)
|
||||||
elif options.versions:
|
elif options.versions:
|
||||||
doversions(options)
|
doversions(options)
|
||||||
elif options.allversions(options):
|
elif options.allversions:
|
||||||
doallversions(options)
|
doallversions(options)
|
||||||
elif options.install or options.update:
|
elif options.install or options.update:
|
||||||
if not options.__all and len(options.TOOLS)==0:
|
if not options.__all and len(options.TOOLS)==0:
|
||||||
|
|
Loading…
Reference in New Issue