diff --git a/README.md b/README.md index 5b7cef5..198f2c3 100644 --- a/README.md +++ b/README.md @@ -134,3 +134,5 @@ Currently only Linux is supported. It should work on all Unix like OS. For other * webinstall * [has](https://github.com/kdabir/has), check versions of commands * [nvchecker](https://github.com/kdabir/has), for checking if a new version of some software has been released. +* [fig/autocomplete](https://github.com/withfig/autocomplete), contributed completion for hundreds of CLI + diff --git a/TODO.md b/TODO.md index 3298b28..48aa81e 100644 --- a/TODO.md +++ b/TODO.md @@ -6,6 +6,11 @@ - [ ] latest version, github strategy - [ ] intall, github strategy +- [ ] code style + +- [ ] tests + - [ ] + - [ ] choose forge(s), one will be the golden source of the catalog * github * gitlab @@ -16,11 +21,11 @@ - [ ] reserve name - [ ] GH - - [ ] Pypy + - [ ] Pypi - [ ] release - [ ] GH release - - [ ] pypy package + - [ ] pypi package - [ ] open issue in managed tool forge for them to add cliget install method @@ -31,8 +36,14 @@ - [ ] existence of `.local`, `.local/programs`, `.local/bin` - [ ] that permissions are ok - [ ] that `.local/bin` are in `PATH` - - [ ] allow to force installation version - [ ] allow to list versions - - [ ] improve search algorithm, fuzzing + - [ ] allow to force installation of a given version + - [-] improve search algorithm, fuzzing + - [+] adjust fuzzing formula => lower the weight of title + - [ ] build tests + - [ ] async loading of versions ; fill output when available + - [ ] cache GH response for a given time + + diff --git a/catalog.yaml b/catalog.yaml index 8268276..20fbcf9 100644 --- a/catalog.yaml +++ b/catalog.yaml @@ -38,6 +38,10 @@ cliget: desc: install various tools in your user profile github: setop/cliget +dslcad: + desc: a programming language & interpreter for building 3D models + github: DSchroer/dslcad + exa: desc: enhanced ls @@ -149,6 +153,11 @@ outrun: github: Overv/outrun pip: +pandoc: + desc: Universal markup converter + website: https://pandoc.org/ + github: jgm/pandoc + pet: desc: Simple command-line snippet manager github: knqyf263/pet @@ -157,6 +166,12 @@ pylufic: inst: direct desc: Let's upload that file CLI +qjs: + desc: a small and embeddable Javascript engine. + name: QuiskJS + releases: http://ffmpeg.org/releases/ + + qr: desc: generate qr code github: Y2Z/qr @@ -199,7 +214,7 @@ stork: tab: desc: A modern text/number processing language for the shell. website: http://tab-lang.xyz/ - github: https://bitbucket.org/tkatchev/tab + bitbucket: tkatchev/tab task: github: go-task/task @@ -225,6 +240,10 @@ tqdm: desc: a better pv pip: +trivy: + desc: Find vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more + github: aquasecurity/trivy + ttyd: desc: share terminal over the web github: tsl0922/ttyd diff --git a/cliget.py b/cliget.py index 166282e..d27ea0a 100644 --- a/cliget.py +++ b/cliget.py @@ -3,15 +3,19 @@ Usage: cliget [-v] [-c URL] list cliget [-v] [-c URL] search PAT - cliget [-v] [-c URL] install TOOL ... - cliget [-v] [-c URL] update [--all] [TOOL ...] + 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 for tools in the catalog with the given pattern -install TOOL install some tools -update list all updatable tools -update TOOL update tools -update --all update all updatable tools +list list all installed tools +search search catalog for tools with the given pattern +versions TOOLS list current and latest versions of some tools +allversions TOOL list all versions of a tool +install TOOLS install some tools +update list all updatable tools +update TOOLS update tools +update --all update all updatable tools Options: @@ -41,13 +45,13 @@ class DotDict(dict): def __getattr__(self, name): return self[name] if name in self else None -def loadcatalog(options)->dict: +def _load_catalog(options)->dict: catalog=options.get('__catalog', 'catalog.yaml') o = load(open(catalog), SafeLoader) trace(o) return { k:DotDict(v) for k,v in o.items()} -def find_semver(s:str) -> VersionInfo: +def _find_semver(s:str) -> VersionInfo: ver = VersionInfo(0,0,0) try: ver = VersionInfo.parse(s) @@ -59,17 +63,24 @@ def find_semver(s:str) -> VersionInfo: return ver -_version = lambda cmd: run([cmd, '--version'], input='', text=True, capture_output=True, check=True, timeout=0.1).stdout.split('\n')[0] +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] + ver = _find_semver(first_line) + except Exception as e: + trace("run error", e) + return ver def _internal_list(options) -> tuple[str,VersionInfo]: """list installed tools and their version""" - ctl = loadcatalog(options) + ctl = _load_catalog(options) for cli, props in ctl.items(): # search in path try: - vers = _version(cli) + vers = _local_version(cli) trace(cli, vers) - yield cli, props, find_semver(vers) + yield cli, props, vers except CalledProcessError: trace(cli, "call error") except TimeoutExpired: @@ -83,13 +94,13 @@ def dolist(options): def dosearch(options): pat = options.PAT - ctl = loadcatalog(options) + ctl = _load_catalog(options) L = [] for cli, props in ctl.items(): trace(cli, props) rtitle = fuzz.ratio(cli, pat) - rdesc = fuzz.partial_ratio(props['desc'], pat) - score = 2 * rtitle + rdesc + rdesc = fuzz.partial_token_set_ratio(props['desc'], pat) + score = 0.2 * rtitle + rdesc L.append((cli, props['desc'], score)) L = sorted(L, key=lambda x: -x[-1]) # TODO format a as table @@ -104,15 +115,48 @@ def dolistupdate(options): pass +def _gh_versions(repo:str) -> [VersionInfo|None]: + [owner, repo] = repo.split("/") + url = f'https://api.github.com/repos/{owner}/{repo}/releases' + response = requests.get(url) + return [ _find_semver(o.get("tag_name")) for o in response.json()] + def _gh_version(repo:str) -> [VersionInfo|None]: [owner, repo] = repo.split("/") url = f'https://api.github.com/repos/{owner}/{repo}/releases/latest' response = requests.get(url) - return response.json().get("name") + return _find_semver(response.json().get("tag_name")) + +def doversions(options): + tools = options.TOOLS + ctl = _load_catalog(options) + for cli in tools: + if cli in ctl: + props = ctl[cli] + rver = _gh_version(props.github) if props.github else VersionInfo(0) + lver = _local_version(cli) + trace(lver) + trace(rver) + print(f"{cli} | {lver} | {rver}") + else: + warn(f'{tool} not in catalog') + +def doallversions(options): + tool = options.TOOL + ctl = _load_catalog(options) + if tool in ctl: + props = ctl[tool] + if props.github: + vers = _gh_versions(props.github) + trace(vers) + print("\n".join(vers)) + else: + warn(f'{tool} not in catalog') + def doinstall(options): - tools = options.TOOL - ctl = loadcatalog(options) + tools = options.TOOLS + ctl = _load_catalog(options) for tool in tools: if tool in ctl: props = ctl[tool] @@ -122,6 +166,13 @@ def doinstall(options): else: warn(f'{tool} not in catalog') +def perform_install(cli, repo): + # get arch and os + # dl asset + # mkdirs + # unpack + # symlink + if __name__ == '__main__': if "DEBUG" in os.environ: @@ -136,10 +187,14 @@ if __name__ == '__main__': dolist(options) elif options.search: dosearch(options) + elif options.versions: + doversions(options) + elif options.allversions(options): + doallversions(options) elif options.install or options.update: - if not options.__all and len(options.TOOL)==0: + if not options.__all and len(options.TOOLS)==0: dolistupdate(options) - elif len(options.TOOL)>0: + elif len(options.TOOLS)>0: doinstall(options) else: print("not implemented") diff --git a/requirements.txt b/requirements.txt index 0a45e45..bd8438b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ pyyaml docopt semver -fuzzywuzzy -python-Levenshtein +#fuzzywuzzy +thefuzz +#python-Levenshtein requests