forked from Botspot/pi-apps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpkg-install
executable file
·186 lines (147 loc) · 6.58 KB
/
pkg-install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/bin/bash
DIRECTORY="$(readlink -f "$(dirname "$0")")"
trap "exit 1" TERM
export TOP_PID=$$
error() {
echo -e "\e[91m$1\e[39m" 1>&2
kill -w -s TERM $TOP_PID
exit 1
}
#$1 is a quotation-marked, space-seperated list of package names.
#$2 is the path to the program folder being installed.
#Example usage: ~/pi-apps/pkg-install "gparted buffer expect" ~/pi-apps/apps/Arduino
PKG_LIST="$1" #quotation-marked, space-seperated list of package names to install
app="$(basename "$2")" #remove any slashes to just get program name
reduceapt() { #remove unwanted lines from apt output
grep -v "apt does not have a stable CLI interface.\|Reading package lists...\|Building dependency tree\|Reading state information...\|Need to get\|After this operation,\|Get:\|Fetched\|Selecting previously unselected package\|Preparing to unpack\|Unpacking \|Setting up \|Processing triggers for "
}
echo "Running pkg-install..."
if [ -z "$PKG_LIST" ];then
error "No package names specified to pkg-install!"
elif [ -z "$app" ];then
error "No app name specified to pkg-install!"
fi
echo -n "Waiting until APT locks are released... "
{
while sudo fuser /var/lib/dpkg/lock &>/dev/null ; do
sleep 0.5
done
while sudo fuser /var/lib/apt/lists/lock &>/dev/null ; do
sleep 0.5
done
if [ -f /var/log/unattended-upgrades/unattended-upgrades.log ]; then
while sudo fuser /var/log/unattended-upgrades/unattended-upgrades.log &>/dev/null ; do
sleep 0.5
done
fi
echo "Done"
}
#sudo apt update
{
echo -e "Running \e[4msudo a\e[0mp\e[4mt u\e[0mp\e[4mdate\e[0m..."
output="$(sudo LANG=C LC_ALL=C apt update 2>&1)"
exitcode=$?
#inform user about autoremovable packages
if [ ! -z "$(echo "$output" | grep 'autoremove to remove them' )" ];then
echo -e "\e[33mSome packages are unnecessary.\e[39m Please consider running \e[4msudo apt autoremove\e[0m."
fi
#inform user packages are upgradeable
if [ ! -z "$(echo "$output" | grep 'packages can be upgraded' )" ];then
echo -e "\e[33mSome packages can be upgraded.\e[39m Please consider running \e[4msudo apt full-upgrade\e[0m."
fi
#exit on apt error
errors="$(echo "$output" | grep '^[(W)|(E)|(Err]:')"
if [ $exitcode != 0 ] || [ ! -z "$errors" ];then
echo -e "\e[91mpkg-install failed to run \e[4msudo apt update\e[0m\e[39m!"
echo -e "APT reported these errors:\n\e[91m$errors\e[39m"
exit 1
fi
}
#workarounds for local debs
{
for package in $PKG_LIST ;do
#if package begins with / (absolute path)
if [[ "$package" == /* ]];then
#determine the package name from the package filepath
packagename="$(dpkg-deb -I "$package" | grep "^ Package:" | awk '{print $2}')"
[ -z "$packagename" ] && error "pkg-install: failed to determine the package name of $package"
#change PKG_LIST to contain package name instead of package's absolute path
PKG_LIST="$(echo "$PKG_LIST" | sed "s|$package|$packagename|")"
echo -e "\e[97m\nInstalling local $packagename package...\e[39m"
#install it and reduce apt's output and indent the output
(sudo LANG=C LC_ALL=C apt install -y --no-install-recommends "$package" 2>&1 || error "pkg_install: While installing local packages, $package failed to install.") | reduceapt
sudo apt-mark auto "$packagename" || error "pkg-install: failed to mark the $packagename package as autoremovable.\nlocal package path: $package"
fi
done
#now PKG_LIST shouldn't contain any absolute file paths
if [[ "$PKG_LIST" == /* ]];then
error "pkg_install: failed to remove all absolute paths from the package list!\nContents of PKG_LIST:\n$PKG_LIST"
fi
}
#workarounds for regex (using '*')
{
for package in $PKG_LIST ;do
#if package contains *
if echo "$package" | grep -q '*' ;then
echo -e "\e[97m\n$package contains regex. Expanding it...\e[39m"
list="$(apt-cache search "$package" | awk '{print $1}' | grep "$(echo "$package" | tr -d '*')" | tr '\n' ' ')"
#change PKG_LIST to contain package name instead of package's absolute path
PKG_LIST="$(echo "$PKG_LIST" | sed "s|$(echo "$package" | sed 's/*/\\*/g')|$list|g")"
echo -e "\e[97m\npackage list is now: $PKG_LIST\e[39m"
fi
done
#now PKG_LIST shouldn't contain any * characters
if echo "$PKG_LIST" | grep -q '*';then
error "pkg_install: failed to remove all regex from the package list!\nContents of PKG_LIST:\n$PKG_LIST"
fi
}
#format PKG_LIST - remove double spaces, preceding spaces, and trailing spaces.
PKG_LIST="$(echo "$PKG_LIST" | sed 's/ / /g' | sed 's/^ //g' | sed 's/ $//g')"
#create dummy deb
{
#create dummy apt package that depends on the packages this app requires
echo -e "\e[97m\nCreating dummy deb...\e[39m"
#to avoid issues with symbols and spaces in app names, we shasum the app name for use in apt
appnamehash="$(echo "$app" | md5sum | cut -c1-8 | awk '{print $1}')"
rm -rf ~/pi-apps-$appnamehash ~/pi-apps-$appnamehash.deb
mkdir -p ~/pi-apps-$appnamehash/DEBIAN
echo "Maintainer: Pi-Apps team
Name: $app
Description: Dummy package created by pi-apps to install dependencies for the '$app' app
Version: 1.0
Architecture: all
Priority: optional
Section: custom
Depends: $(echo "$PKG_LIST" | tr -d ',' | sed 's/ /, /g')
Package: pi-apps-$appnamehash" > ~/pi-apps-$appnamehash/DEBIAN/control
dpkg-deb --build ~/pi-apps-$appnamehash || error "pkg-install: failed to create dummy deb pi-apps-$appnamehash"
}
#install dummy deb
{
#ensure dummy deb isn't already installed
if dpkg -l pi-apps-$appnamehash &>/dev/null ;then
echo -e "\e[97m\nDummy deb is already installed. Uninstalling it first...\e[39m"
sudo apt purge -y pi-apps-$appnamehash || error "Failed to purge dummy deb (pi-apps-$appnamehash)"
sudo apt update &>/dev/null
fi
echo -e "\e[97m\nInstalling dummy deb...\e[39m"
output="$(sudo apt-get install -y --no-install-recommends ~/pi-apps-$appnamehash.deb 2>&1 | reduceapt | tee /dev/stderr )"
rm -f ~/pi-apps-$appnamehash.deb
echo -e "\e[97m\nApt finished.\e[39m"
errors="$(echo "$output" | grep '^[(W)|(E)|(Err]:')"
if [ ! -z "$errors" ];then
echo -e "\e[91mFailed to install the packages!\e[39m"
echo -e "APT reported these errors:\n\e[91m$errors\e[39m"
exit 1
fi
}
#re-check package list. This time it should be blank.
{
INSTALL_LIST="$(sudo LANG=C LC_ALL=C apt-get install --no-install-recommends --dry-run $PKG_LIST 2>&1 | sed -n '/The following NEW packages/,/to remove/p' | sed -e '2,$!d' -e '$d' | tr -d '*' | tr '\n' ' ' | sed 's/The following.*//')"
if [ ! -z "$INSTALL_LIST" ];then
error "APT did not exit with an error, but these packages failed to install somehow:\n$INSTALL_LIST\e[39m"
else
echo -e "\e[32mAll packages were installed succesfully.\e[39m"
fi
}
rm -rf ~/pi-apps-$appnamehash #remove dummy deb creation folder if all went well