#!/usr/bin/env python3 """Troca os arquivos de ponteiro do Git LFS de uma arvore (as imagens .png do Clean Line, que o tarball do GitHub traz como ponteiros de ~130 bytes) pelos objetos reais, pedidos pela API em lote do LFS do proprio repositorio do autor. Cada objeto e conferido pelo sha256 do ponteiro (o oid do LFS e o sha256 do conteudo) e pelo tamanho. Uso: lfs-fetch.py [ ...]""" import hashlib, json, os, sys, urllib.request repo, dirs = sys.argv[1], sys.argv[2:] MAGIC = b"version https://git-lfs.github.com/spec/v1\n" todo = {} for d in dirs: for base, _, files in os.walk(d): for f in files: p = os.path.join(base, f) with open(p, "rb") as fh: head = fh.read(200) if head.startswith(MAGIC): lines = dict(l.split(" ", 1) for l in head.decode().strip().split("\n")[1:]) todo.setdefault((lines["oid"].split(":", 1)[1].strip(), int(lines["size"])), []).append(p) if not todo: print("nenhum ponteiro LFS") sys.exit(0) req = urllib.request.Request( "https://github.com/%s.git/info/lfs/objects/batch" % repo, data=json.dumps({"operation": "download", "transfers": ["basic"], "objects": [{"oid": o, "size": s} for (o, s) in todo]}).encode(), headers={"Accept": "application/vnd.git-lfs+json", "Content-Type": "application/vnd.git-lfs+json"}) res = json.load(urllib.request.urlopen(req, timeout=60)) for obj in res["objects"]: key = (obj["oid"], obj["size"]) if "error" in obj: sys.exit("LFS: %s: %s" % (obj["oid"], obj["error"])) data = urllib.request.urlopen(obj["actions"]["download"]["href"], timeout=60).read() if hashlib.sha256(data).hexdigest() != obj["oid"] or len(data) != obj["size"]: sys.exit("LFS: conteudo de %s nao confere" % obj["oid"]) for p in todo[key]: with open(p, "wb") as fh: fh.write(data) print("objetos LFS baixados e conferidos:", len(todo), "em", sum(len(v) for v in todo.values()), "arquivos")