Last active
October 11, 2015 13:37
-
-
Save silvasur/3867249 to your computer and use it in GitHub Desktop.
Yet another AUR package downloader...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# -*- coding: utf-8 -*- | |
# aurfetch.py - My small helper script for fetching AUR packages and automatically update them | |
# It doesn't do the fancier stuff like automagically fetch dependencies. | |
# Have fun :-) | |
# | |
# License: [Do What The Fuck You Want To Public License](http://en.wikipedia.org/wiki/WTFPL) | |
###### START EDITING HERE | |
AUR_BASE = "https://aur.archlinux.org" | |
DBPATH = "~/.aurfetch.db" | |
BUILDPATH = "~/build" | |
MAKEPKG_OPTIONS = ["-s", "-c"] | |
###### END EDITING HERE | |
# (At least if you only want to configure :-) ) | |
import urllib2 | |
import json | |
import os, sys | |
import subprocess | |
from functools import partial | |
from uuid import uuid4 | |
from shutil import rmtree | |
try: | |
import cPickle as pickle | |
except ImportError: | |
import pickle | |
class AURQueryError(Exception): pass | |
class NoAURDataAvail(Exception): pass | |
def query(cmd, arg): | |
urlh = urllib2.urlopen("{base}/rpc.php?type={cmd}&arg={arg}".format( | |
base=AUR_BASE, | |
cmd=urllib2.quote(cmd), | |
arg=urllib2.quote(str(arg)) | |
)) | |
raw = urlh.read() | |
urlh.close() | |
data = json.loads(raw) | |
if data["type"] == "error": | |
raise AURQueryError(data["results"]) | |
if (data["type"] == "info") and (data["resultcount"] == 0): | |
raise NoAURDataAvail("No results.") | |
return data["results"] | |
search = partial(query, "search") | |
info = partial(query, "info") | |
multiinfo = partial(query, "multiinfo") | |
msearch = partial(query, "msearch") | |
def load_db(): | |
try: | |
with open(os.path.expanduser(DBPATH), "rb") as fh: | |
return pickle.load(fh) | |
except IOError: | |
return {} | |
def save_db(db): | |
with open(os.path.expanduser(DBPATH), "wb") as fh: | |
pickle.dump(db, fh) | |
db = load_db() | |
def indent(s, by): | |
return u"\n".join(by + l for l in s.splitlines()) | |
def format_info(inf): | |
global db | |
flags = "" | |
if inf["OutOfDate"] == "1": | |
flags += "!" | |
if inf["ID"] in db: | |
if int(inf["LastModified"]) > db[inf["ID"]]["timestamp"]: | |
flags += "*" | |
else: | |
flags += "~" | |
if len(flags) > 0: | |
flags += " " | |
return u"{flags}{name} {version} (ID:{id}, Votes:{votes})\n{desc}".format( | |
flags=flags, | |
name=inf["Name"], | |
version=inf["Version"], | |
id=inf["ID"], | |
votes=inf["NumVotes"], | |
desc=indent(inf["Description"], u"| ") | |
) | |
def format_search(search_result): | |
global db | |
search_result.sort(key=lambda x: int(x["NumVotes"]), reverse=True) | |
return "\n\n".join(map(format_info, search_result)) | |
def build(path): | |
global MAKEPKG_OPTIONS | |
olddir = os.getcwd() | |
os.chdir(path) | |
makepkg = subprocess.Popen(["makepkg"] + MAKEPKG_OPTIONS) | |
rv = makepkg.wait() | |
if rv != 0: | |
print "\033[41m\033[37m\033[1m" + "Automatic build failed. I will open a shell in the packagepath for you. Exit it to continue." + "\033[0m" | |
shell = subprocess.Popen([os.environ.get("SHELL", "/bin/sh")]) | |
shell.wait() | |
print "\033[43m\033[37m\033[1m" + "Leaving build shell..." + "\033[0m" | |
os.chdir(olddir) | |
def fetch(inf): | |
global db | |
tmpname = os.path.join("/tmp", "aurfetch_py_pkg_" + str(uuid4())) | |
url = AUR_BASE + inf["URLPath"] | |
curl = subprocess.Popen(["curl", "-o", tmpname, url]) | |
if curl.wait() != 0: | |
print >>sys.stderr, "Could not fetch" | |
return 1 | |
tar = subprocess.Popen(["tar", "-C", os.path.expanduser(BUILDPATH), "-x", "-v", "-z", "--overwrite", "-f", tmpname]) | |
if tar.wait() != 0: | |
print >>sys.stderr, "Could not extract" | |
os.unlink(tmpname) | |
return 1 | |
path = os.path.join(os.path.expanduser(BUILDPATH), inf["Name"]) | |
db[int(inf["ID"])] = { | |
"name": inf["Name"], | |
"timestamp": int(inf["LastModified"]), | |
"path": path | |
} | |
save_db(db) | |
print "Package is now at " + path | |
if askyesno("Build '{}' now? ".format(inf["Name"])): | |
build(path) | |
return 0 | |
def unfetch(ID): | |
global db | |
data = db[ID] | |
rmtree(data["path"]) | |
del db[ID] | |
save_db(db) | |
return 0 | |
def askyesno(prompt): | |
while True: | |
try: | |
usrin = raw_input(prompt)[0] | |
except IndexError: | |
continue | |
if usrin in "yYjJ": | |
return True | |
elif usrin in "nN": | |
return False | |
if __name__ == '__main__': | |
if len(sys.argv) < 2: | |
print >>sys.stderr, "Commands: search fetch unfetch update list" | |
sys.exit(1) | |
if sys.argv[1] == "search": | |
try: | |
print format_search(search(sys.argv[2])).encode("utf-8") | |
except IndexError: | |
print >>sys.stderr, "search needs a keyword" | |
sys.exit(1) | |
elif sys.argv[1] == "list": | |
pkglist = list(db.items()) | |
pkglist.sort(key=lambda x: x[1]["name"]) | |
for ID, data in pkglist: | |
print data["name"] | |
elif sys.argv[1] == "fetch": | |
if len(sys.argv) < 3: | |
print >>sys.stderr, "fetch needs a keyword" | |
sys.exit(1) | |
inf = info(sys.argv[2]) | |
print format_info(inf) | |
print "" | |
if askyesno("Fetch? "): | |
sys.exit(fetch(inf)) | |
elif sys.argv[1] == "unfetch": | |
if len(sys.argv) < 3: | |
print >>sys.stderr, "unfetch needs a keyword" | |
sys.exit(1) | |
try: | |
ID, data = [(ID, data) for ID, data in db.items() if data["name"] == sys.argv[2]][0] | |
except KeyError: | |
print >>sys.stderr, "{} was not fetched (at least not with aurfetch.py)".format(sys.argv[2]) | |
if askyesno("Really unfetch? "): | |
sys.exit(unfetch(ID)) | |
elif sys.argv[1] == "update": | |
print "This might take a while..." | |
toupdate = [] | |
for ID, data in db.items(): | |
try: | |
inf = info(ID) | |
except NoAURDataAvail: | |
print >>sys.stderr, "\033[41m\033[37m\033[1m"+"Package {p} not found. Was probably removed.".format(p=data["name"])+"\033[0m" | |
if askyesno("Do you want to unfetch it? "): | |
unfetch(ID) | |
continue | |
if int(inf["LastModified"]) > data["timestamp"]: | |
print "{} will be updated".format(inf["Name"]) | |
toupdate.append(inf) | |
else: | |
print "{} is up to date".format(inf["Name"]) | |
failed = [] | |
yay = [] | |
for tu in toupdate: | |
if fetch(tu) != 0: | |
failed.append(tu["Name"]) | |
else: | |
yay.append(tu["Name"]) | |
if len(failed) > 0: | |
print "" | |
print "These packages could not be updated:" | |
for f in failed: | |
print "*", f | |
sys.exit(1) | |
if len(yay) > 0: | |
print "" | |
print "These packages were updated:" | |
for y in yay: | |
print "*", y |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment