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!
This commit is contained in:
Geoffrey Frogeye 2016-11-19 23:51:30 +01:00
parent d421235c17
commit e1d7223175
4 changed files with 225 additions and 356 deletions

224
scripts/debloc.sh Executable file
View file

@ -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
}

View file

@ -1,3 +1,3 @@
source ~/.scripts/proxy.sh
source ~/.scripts/locinst.sh
source ~/.scripts/debloc.sh
alias beep=~/.scripts/beep.sh

View file

@ -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]))

View file

@ -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