From e1d7223175750920141dae5257f4119173a99455 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sat, 19 Nov 2016 23:51:30 +0100 Subject: [PATCH] Bye bye locinst, hello debloc! Well, for what I wanted to do, locinst really was badly designed and overkill... I'm really happy with how debloc turned out! --- scripts/debloc.sh | 224 +++++++++++++++++++++++++++++++++++++++++++++ scripts/index.sh | 2 +- scripts/locinst.py | 152 ------------------------------ scripts/locinst.sh | 203 ---------------------------------------- 4 files changed, 225 insertions(+), 356 deletions(-) create mode 100755 scripts/debloc.sh delete mode 100755 scripts/locinst.py delete mode 100755 scripts/locinst.sh diff --git a/scripts/debloc.sh b/scripts/debloc.sh new file mode 100755 index 0000000..b359f8e --- /dev/null +++ b/scripts/debloc.sh @@ -0,0 +1,224 @@ +#!/usr/bin/env bash + +DEBLOC_DB=$HOME/.config/debloc +DEBLOC_ROOT=$HOME/.debloc + +# TODO Configurable +DEBIAN_MIRROR=http://debian.polytech-lille.fr/debian +DEBIAN_MIRROR=http://ubuntu.mirrors.ovh.net/ftp.ubuntu.com/ubuntu + +mkdir -p $DEBLOC_DB &> /dev/null +mkdir -p $DEBLOC_ROOT &> /dev/null + +export PATH="$DEBLOC_ROOT/usr/bin:$DEBLOC_ROOT/usr/games/:$PATH" +export LD_LIBRARY_PATH="$DEBLOC_ROOT/lib:$LD_LIBRARY_PATH" +export PYTHONPATH="$DEBLOC_ROOT/usr/lib/python3:$PYTHONPATH" +export QT_QPA_PLATFORM_PLUGIN_PATH="$DEBLOC_ROOT/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms" + +# Tell if a package exists +function _debloc-exists { # package + LANG=C apt-cache show $1 &> /dev/null + if [ $? == 0 ]; then + return 1 + else + return 0 + fi +} + +# Return the real package associated with a virtual package +# If not a virtual package, return the input +function _debloc-filterVirtual { # package + pkg=$1 + LANG=C apt-cache policy $1 | grep "Candidate" | grep "(none)" > /dev/null + if [ $? == 0 ]; then + # TODO This is not really accurate + LANG=C apt-cache showpkg $pkg | tail -1 | cut -d ' ' -f 1 + else + echo $pkg + fi + return 0 +} + +# Tell if a package is installed via debloc +function _debloc-locallyInstalled { # package + if [ -e $DEBLOC_DB/$1 ]; then + return 1 + else + return 0 + fi +} + +# Tell if a package is installed system-wide +function _debloc-globallyInstalled { # package + LANG=C apt-cache policy $1 | grep "Installed" | grep "(none)" > /dev/null + return $? +} + +# Get informations about a package +function _debloc-packageShow { # package + LANG=C apt-cache show $1 | while read line; do + if [ "$line" == '' ]; then + break + fi + echo "$line" + done + return 0 +} + +# Get the path of a package +function _debloc-packagePath { # package + _debloc-packageShow $1 | grep "^Filename:" | head -1 | cut -d ':' -f 2 | sed -e 's/^[[:space:]]*//' + return 0 +} + +# Get the md5sum of a package +function _debloc-packageMd5sum { # package + _debloc-packageShow $1 | grep "^MD5sum:" | cut -d ':' -f 2 | sed -e 's/^[[:space:]]*//' + return 0 +} + +# Update symbolics links in $DEBLOC_ROOT/lib +function _debloc-ldconfig { + mkdir -p $DEBLOC_ROOT/lib &> /dev/null + rm -f $DEBLOC_ROOT/lib/* &> /dev/null + if [ -e $DEBLOC_ROOT/usr/lib ]; then + find $DEBLOC_ROOT/usr/lib -type f -name "*.so*" | while read lib; do + ln --symbolic --force "$lib" "$DEBLOC_ROOT/lib/$(basename $lib)" + done + find $DEBLOC_ROOT/usr/lib -type l -name "*.so*" | while read link; do + yes | cp --force --no-dereference --preserve=links "$link" "$DEBLOC_ROOT/lib/" &> /dev/null + done + fi + +} + +# Install debian archive +function _debloc-installDeb { # path + TMP_DIR=$(mktemp -d) &> /dev/null + $(cd $TMP_DIR; ar x "$1") + TAR_FILE=$(find $TMP_DIR -type f -name "data.tar.*" | head -1) + if [ -e "$TAR_FILE" ]; then + # Output for DB saving + tar tf $TAR_FILE + tar xf $TAR_FILE -C $DEBLOC_ROOT + + # _debloc-ldconfig + mkdir -p $DEBLOC_ROOT/lib &> /dev/null + tar tf $TAR_FILE | grep '^./usr/lib/' | grep '\.so' | while read file; do + lib=$(readlink -f $DEBLOC_ROOT/$file) + if [ -f $lib ]; then + ln --symbolic --force "$lib" "$DEBLOC_ROOT/lib/$(basename $file)" + fi + if [ -h $lib ]; then + yes | cp --force --no-dereference --preserve=links "$(basename $link)" "$DEBLOC_ROOT/lib/" &> /dev/null + fi + done + fi + + rm -rf $TMP_DIR &> /dev/null + return 0 + +} + +# Install package +function _debloc-install { # package + pkg=$1 + + echo "→ Downloading" + url=${DEBIAN_MIRROR}/$(_debloc-packagePath $pkg) + DEB_FILE=$(mktemp) &> /dev/null + wget "$url" --quiet -O $DEB_FILE + if [ $? != 0 ]; then + echo "→ Failed!" + return 4 + fi + + echo "→ Verifying sums" + theo=$(_debloc-packageMd5sum $pkg) + real=$(md5sum $DEB_FILE | cut -d ' ' -f 1) + if [ "$theo" != "$real" ]; then + rm -f $DEB_FILE &> /dev/null + echo "→ Failed!" + return 5 + fi + + echo "→ Installing" + _debloc-installDeb $DEB_FILE > $DEBLOC_DB/$pkg + + echo "→ Done!" + rm -f $DEB_FILE &> /dev/null + return 0 +} + +# Get the dependencies of a package +function _debloc-packageDeps { # package + LANG=C apt-cache show $1 | grep '^Depends:' | sed 's/Depends: //' | sed 's/, /\n/g' | cut -d ' ' -f 1 + return 0 +} + +# Install package with dependencies +function _debloc-installDeps { # package + pkg=$1 + echo "Installing $pkg" + _debloc-packageDeps $pkg | while read dep; do + dep=$(_debloc-filterVirtual $dep) + _debloc-locallyInstalled $dep + if [ $? == 1 ]; then + echo "- Dependency $dep is already installed with Debloc" + continue + fi + _debloc-globallyInstalled $dep + if [ $? == 1 ]; then + echo "- Dependency $dep is already installed on the system" + continue + fi + _debloc-installDeps $dep | while read line; do echo "- $line"; done + done + _debloc-install $pkg + return 0 +} + +# Install package with dependencies (user version with verifications) +function debloc-install { # package + for pkg in $*; do + pkg=$(_debloc-filterVirtual $pkg) + _debloc-exists $pkg + if [ $? == 0 ]; then + echo "Unknown package $pkg" + continue + fi + _debloc-locallyInstalled $pkg + if [ $? == 1 ]; then + echo "Package $pkg is already installed with Debloc" + continue + fi + _debloc-globallyInstalled $pkg + if [ $? == 1 ]; then + echo "Package $pkg is already installed on the system" + continue + fi + _debloc-installDeps $pkg + done + return 0 + +} + +# Install debian archive (user version with verifications) +function debloc-deb { # path + for path in $*; do + if [ ! -f "$path" ]; then + echo "$path is not a file" + return 6 + fi + echo "Installing $(basename $path)" + _debloc-installDeb "$(readlink -f $path)" > $DEBLOC_DB/$(basename $path) + done + return 0 + +} + +# Remove every package installed with Debloc +function debloc-flush { + rm -rf $DEBLOC_ROOT/* &> /dev/null + rm -f $DEBLOC_DB/* &> /dev/null +} diff --git a/scripts/index.sh b/scripts/index.sh index 3d7f7e8..87b928b 100644 --- a/scripts/index.sh +++ b/scripts/index.sh @@ -1,3 +1,3 @@ source ~/.scripts/proxy.sh -source ~/.scripts/locinst.sh +source ~/.scripts/debloc.sh alias beep=~/.scripts/beep.sh diff --git a/scripts/locinst.py b/scripts/locinst.py deleted file mode 100755 index d0b75ea..0000000 --- a/scripts/locinst.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python3 - -import json -import urllib.request -import subprocess -import os -import sys -import re - -LOCINST_DIR=os.environ['HOME']+'/.locinst' -LOCINST_DB=os.environ['HOME']+'/.config/locinst' -LOCINST_TMP='/tmp/locinst' - -DEBIAN_MIRROR = 'http://debian.polytech-lille.fr/debian/' - -def globallyInstalled(name): - try: - a = subprocess.check_output(['apt-cache', 'policy', name]).decode('utf8').strip().lower() - return 'installé\xa0: (aucun)' not in [b.strip() for b in a.split('\n')] - except FileNotFoundError: - return False - -def locallyInstalled(name): - return False - -def debian_install(name): - def findPackage(name): - raw = urllib.request.urlopen('http://sources.debian.net/api/search/' + name + '/') - data = json.loads(raw.read().decode('utf8')) - results = data['results'] - if results['exact']: - return True - else: - print("Package " + name + " does not exist" + (", however you might want to try one of the following: " + ", ".join([pkg['name'] for pkg in results['other']]) if results['other'] else "") + ".") - return False - - def findVersion(name): - raw = urllib.request.urlopen('http://sources.debian.net/api/src/' + name + '/') - data = json.loads(raw.read().decode('utf8')) - - def findByCodename(codename): - goodVersions = [version['version'] for version in data['versions'] if codename in version['suites']] - return goodVersions[0] if len(goodVersions) else False - - try: - codename = subprocess.check_output(['lsb_release', '--codename', '--short']).decode('utf8').strip().lower() - choice = findByCodename(codename) - if choice: - return choice - except FileNotFoundError: - pass - - return findByCodename('jessie') - - def parseInfos(control): - paragraph = {} - key = '' - - for line in control.strip().split('\n'): - if re.match('\s', line[0]): - try: - paragraph[key] += '\n' + line.strip() - except KeyError: - pass - elif re.match('^[\w-]+\:', line): - key = line.split(':')[0].lower() - paragraph[key] = line[len(key)+1:].strip() - - return paragraph - - def getArch(): - arch = subprocess.check_output(['uname', '--machine']).decode('utf8').strip().lower() - if arch == 'x86_64': - arch = 'amd64' - return arch - - - def download(name, version): - arch = getArch() - filename = name + '_' + version + '_' + arch - - print("Downloading " + filename + "...") - url = DEBIAN_MIRROR + 'pool/main/' + (name[0] if name[0:3] != 'lib' else name[0:4]) + '/' + name + '/' + filename + '.deb' - debfile = LOCINST_TMP + '/' + filename + '.deb' - urllib.request.urlretrieve(url, debfile) - - extractdir = LOCINST_TMP + '/' + filename - try: - os.mkdir(extractdir) - except FileExistsError: - pass - subprocess.check_call(['ar', 'x', debfile], cwd=extractdir) - subprocess.check_call(['rm', '-rf', debfile], stdout=subprocess.DEVNULL) - - controltar = [a for a in os.listdir(extractdir) if a.split('.')[:2] == ['control', 'tar']][0] - subprocess.check_call(['tar', 'xf', extractdir + '/' + controltar], cwd=extractdir) - with open(extractdir + '/control') as controlfile: - control = controlfile.read() - - finaldir = LOCINST_TMP + '/' + name - try: - os.mkdir(finaldir) - except FileExistsError: - pass - datatar = [a for a in os.listdir(extractdir) if a.split('.')[:2] == ['data', 'tar']][0] - subprocess.check_call(['tar', 'xf', extractdir + '/' + datatar], cwd=finaldir) - - subprocess.check_call(['rm', '-rf', extractdir]) - return { - 'final': finaldir, - 'control': control - } - - - if not findPackage(name): - return 4 - - if globallyInstalled(name): - return 5 - - print("Installing " + name + "...") - version = findVersion(name) - - if not version: - return 6 - - d = download(name, version) - finaldir, control = d['final'], d['control'] - - infos = parseInfos(control) - - if 'depends' in infos: - for dep in infos['depends'].split(', '): - dep = dep.strip().split(' ')[0] - if not globallyInstalled(dep): - # print("Installing " + dep + " as a dependency") - # debian_install(dep) - - # TODO For compatibility until locinst.sh is rewritten - try: - subprocess.check_call([os.environ['HOME'] + '/.scripts/locinst.sh', 'debian', dep]) - except subprocess.CalledProcessError: - pass - - if 'recommends' in infos: - for rec in infos['recommends'].split(','): - print(name + " suggests you to install: " + rec.strip()) - - return 0 - -# TODO For compatibility until locinst.sh is rewritten -exit(debian_install(sys.argv[1])) diff --git a/scripts/locinst.sh b/scripts/locinst.sh deleted file mode 100755 index e9196b5..0000000 --- a/scripts/locinst.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/bin/bash -# Local package installer - -# Config -export LOCINST_DIR="$HOME/.locinst/" -#export LOCINST_DB="$XDG_CONFIG_HOME/locinst/" -export LOCINST_DB="$HOME/.config/locinst/" - -# Constants -LOCINST_TMP=/tmp/locinst - -# Path set -export PATH="$LOCINST_DIR/bin:$LOCINST_DIR/usr/bin:$LOCINST_DIR/usr/games/:$PATH" -export LD_LIBRARY_PATH="$LOCINST_DIR/lib:$LOCINST_DIR/usr/lib:$LD_LIBRARY_PATH" - -# Dir set -if [ ! -d "$LOCINST_DIR" ]; then - mkdir -p "$LOCINST_DIR" - # TODO Add symbolic links and stuff -fi -if [ ! -d "$LOCINST_TMP" ]; then - mkdir -p "$LOCINST_TMP" -fi -if [ ! -d "$LOCINST_DB" ]; then - mkdir -p "$LOCINST_DB" -fi - -# Misc functions -function step { # str - echo "--> $1" -} - -function error { # str - echo "ERR $1" -} - - -# Providers -# Args package_destination, package[, extra_info] -# 0: No error, must have put the uncompressed package in $package_destination -# 1: Generic error -# 4: Package not found on repo -# 5: Package already installed on system - -function locinst_arch { - - function getPageLink { # package, arch, repo - echo "https://www.archlinux.org/packages/$3/$2/$1/" - } - - function getDlLink { # package, arch, repo - echo "$(getPageLink $1 $2 $3)download/" - } - - function findPackage { # package - - function testLink { # link - wget -q --max-redirect 0 "$1" -O /dev/null - return $? - } - - function testPackage { # package, arch, repo - link="$(getPageLink $1 $2 $3)" - testLink "$link" - return $? - } - - for repo in community core extra - do - for arch in any $(uname -m) - do - testPackage $1 $arch $repo - if [ $? -eq 0 ]; then - echo "$arch:$repo" - return 0 - fi - done - done - return 1 - } - - dest="$1" - package=$2 - - step "Finding $package" - data=$(findPackage $package) - if [ $? -ne 0 ]; then - return 4 - fi - arch=$(cut -d ":" -f 1 <<< "$data") - repo=$(cut -d ":" -f 2 <<< "$data") - - step "Downloading $repo/$package-$arch" - link=$(getDlLink $package $arch $repo) - destcmp="$LOCINST_TMP/$package.tar.xz" - wget $link -O $destcmp - - step "Extracting package" - tar xf "$destcmp" -C "$dest" - - # TODO Parse .PKGINFO for dependency - # TODO Check if already on system - - rm -f $dest/.MTREE $dest/.PKGINFO - rm "$destcmp" - return 0 -} - -function locinst_pypi { - dest=$1 - shift - tar=$LOCINST_TMP/$1.tar.gz - char=`echo $1 | cut -c1-1` - wget "https://pypi.python.org/packages/source/$char/$1/$1-$2.tar.gz" -O $tar --no-check-certificate - preinst=$LOCINST_TMP/$1-$2/ - tar xf $tar -C $LOCINST_TMP - mkdir -p $dest/lib/python - export PYTHONPATH="$dest/lib/python:$PYTHONPATH" - (cd $preinst; python3 setup.py install --home=$dest) - rm -rf $tar $preinst -} - - - -# Master function - -function locinst { # action package [other_info]* - function remove { # package - package=$1 - index=$LOCINST_DB/$package - # TODO Filter common things, also delete folders - (cd $LOCINST_DIR; cat $index | while read file; do rm -f "$file"; done) - rm -f $index - } - - action=$1 - package=$2 - shift; shift - - dest=$LOCINST_TMP/$package - if [ ! -d "$dest" ]; then - mkdir -p "$dest" - fi - - case $action in - "remove") - step "Removing $package" - remove $package - return $? - ;; - "arch") - locinst_arch $dest $package $* - code=$? - ;; - "pypi") - locinst_pypi $dest $package $* - code=$? - ;; - "debian") - $HOME/.scripts/locinst.py $package $* - code=$? - ;; - *) - #error "I don't know what to do. And don't beg for help with the commands." - return 1 - ;; - esac - - # From now on something was installed - case $code in - 0) - index=$LOCINST_DB/$package - if [ -e $index ]; then - step "Removing old instance of $package" - remove $package - fi - step "Installing $package" - (cd $dest; find . -mindepth 2 >> $index) - cp -r $dest/* $LOCINST_DIR/ - rm -rf $dest - return 0 - ;; - 4) - error "Package not found!" - ;; - 5) - error "Package already installed on system!" - ;; - 6) - error "Could not satisfy version requirements!" - ;; - *) - error "Ugh... Something bad happened" - esac - - # From now on an error has happened - return $code - -} - -if [ -n $1 ]; then - locinst $@ -fi