Skip to content

Instantly share code, notes, and snippets.

@jabbalaci
Created May 12, 2026 18:45
Show Gist options
  • Select an option

  • Save jabbalaci/18285a138017f5ecf8801f1707c849bc to your computer and use it in GitHub Desktop.

Select an option

Save jabbalaci/18285a138017f5ecf8801f1707c849bc to your computer and use it in GitHub Desktop.
nimpkg
#!/usr/bin/env python3
# Author: Laszlo Szathmary, alias Jabba Laci, 2026
import json
import os
import shlex
from pathlib import Path
from pprint import pprint
from subprocess import PIPE, STDOUT, Popen
from time import sleep
import requests
RED = "\033[31m"
GREEN = "\033[32m"
BLUE = "\033[34m"
BOLD = "\033[1m"
RESET = "\033[0m"
HOME = Path.home()
NIMBLE = f"{HOME}/.nimble"
NIMBLE_BIN = f"{NIMBLE}/bin"
PACKAGES_JSON = f"{NIMBLE}/packages_official.json"
# {0}: owner, {1}: repo name; example: https://api.github.com/repos/jabbalaci/nimbang/tags
GITHUB_API = "https://api.github.com/repos/{0}/{1}/tags"
# API endpoint for releases (example): https://api.github.com/repos/nim-lang/langserver/releases/latest
DONT_USE_API_KEY = 0
USE_API_KEY = 1
GITHUB_ACCESS_MODE = DONT_USE_API_KEY
def get_simple_cmd_output(cmd, stderr=STDOUT):
args = shlex.split(cmd)
return Popen(args, stdout=PIPE, stderr=stderr).communicate()[0].decode("utf8")
def get_installed_packages() -> list[str]:
cmd = "nimble list -i"
return get_simple_cmd_output(cmd).strip().splitlines()
def get_binaries() -> list[str]:
return sorted(os.listdir(NIMBLE_BIN))
def get_package_info(name: str) -> dict | None:
def clean(s: str) -> str:
return s.strip().replace('"', "")
d = {}
cmd = f"nimble dump {name}"
for line in get_simple_cmd_output(cmd).strip().splitlines():
try:
left, right = line.split(":")
except ValueError:
# print("")
# print(f"Warning: problem with the package {name}")
# print(f"Tip: check `nimble dump {name}`")
return None
if left == "name":
d["package_name"] = clean(right)
assert d["package_name"] == name # they should be the same
if left == "version":
d["current_version"] = clean(right)
if left == "bin":
d["bin"] = sorted(clean(right).split(", "))
#
#
return d
def build_package_db(installed: list[str]) -> dict:
print("Getting package details", end="")
result = {}
for pkg in installed:
print(".", end="", flush=True)
d = get_package_info(pkg)
if d and len(d["bin"][0]) > 0:
result[pkg] = d
#
print()
return result
def get_tag_name(api_key: str, owner_name: str, repo_name: str) -> str:
print(".", end="", flush=True)
url = GITHUB_API.format(owner_name, repo_name)
headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
try:
r = requests.get(url, headers=headers)
sleep(0.1) # to avoid bashing the server
li = r.json()
d = li[0]
tag = d["name"].replace("v", "")
return tag
except:
print("")
print("Warning: couldn't get the tag info from here:", url)
return ""
#
def get_repo_info(package_names: list[str]) -> dict:
print("Getting latest tag versions", end="")
result = {}
with open(PACKAGES_JSON) as f:
li = json.load(f)
#
api_key = os.environ.get("GITHUB_API_KEY", "")
for d in li:
if d["name"] in package_names:
url = d["url"]
context = {"url": url}
if url.startswith("https://github.com"):
parts = url.split("/")
context["repo_name"] = parts[-1]
context["owner_name"] = parts[-2]
context["latest_tag_name"] = get_tag_name(
api_key, context["owner_name"], context["repo_name"]
)
#
result[d["name"]] = context
#
#
print()
return result
def make_report(db: dict, repo_info: dict) -> None:
template = """
{0}: local version: {1} {2} latest_tag: {3} {4}
""".strip()
to_update = []
for pkg_name, local_d in db.items():
current_version = local_d["current_version"]
repo_d = repo_info[pkg_name]
latest_tag = repo_d["latest_tag_name"]
s1 = current_version
s3 = latest_tag
s0 = (
BOLD + GREEN + pkg_name + RESET
if s1 == s3
else BOLD + RED + pkg_name + RESET
)
if s3 == "":
s3 = BOLD + RED + "???" + RESET
s2 = "==" if s1 == s3 else "!="
s4 = BOLD + GREEN + "[OK]" + RESET if s1 == s3 else BOLD + RED + "[XXX]" + RESET
print(template.format(s0, s1, s2, s3, s4))
if s1 != s3:
to_update.append(pkg_name)
#
#
print("---")
for pkg_name in to_update:
repo_d = repo_info[pkg_name]
latest_tag = repo_d["latest_tag_name"]
if latest_tag:
print(f"* nimble install {pkg_name}@{latest_tag}")
#
#
def check_api_key() -> None:
if GITHUB_ACCESS_MODE == DONT_USE_API_KEY:
print("# not using an API key")
else:
print("# using an API key")
#
if GITHUB_ACCESS_MODE == USE_API_KEY:
try:
api_key = os.environ["GITHUB_API_KEY"]
except KeyError:
print("Error: the env. variable GITHUB_API_KEY is not available")
exit(2)
def main():
check_api_key()
#
installed: list[str] = get_installed_packages()[3:]
bins: list[str] = get_binaries()
# print(installed)
# print("---")
# print(bins)
# print("---")
# d: dict | None = get_package_info("zigcc")
# pprint(d)
# print("---")
db: dict = build_package_db(installed)
pprint(db)
print("---")
repo_info: dict = get_repo_info(list(db.keys()))
pprint(repo_info)
print("---")
make_report(db, repo_info)
##############################################################################
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment