]> git.sev.monster Git - dotfiles.git/blob - bin/apkv
apkv: orphan listing, update comments
[dotfiles.git] / bin / apkv
1 #!/bin/sh
2 # TODO: add duplicate checker to seeif a pkg is in multiple virtual pkgs
3
4 prog=$(basename $0)
5 usage() {
6     echo "\
7 apkv: Quickly add and remove packages to/from Alpine virtual packages.
8
9 Usage:
10     $prog add <packages> <virtual>
11         Add <packages> to virtual package <virtual>. If the virtual package
12         does not exist, it will be created.
13
14     $prog del <packages> <virtual>
15         Remove <packages> from virtual package <virtual>. Empty virtual
16         packages are removed from world. The shorthand '$prog del * <virtual>' can
17         be used to empty <virtual>.
18
19     $prog list [-o | packages]
20         List virtual packages and their contents.
21             packages  List these virtual packages. If empty, list all.
22             -o        Only list orphan packages that are not owned by any
23                       virtual package. Orphans are automatically shown when
24                       listing all packages.
25
26     $prog help
27         Show this help.
28
29 Virtual packages must be prefixed with a dot to be managed with apkv. Virtual
30 package names passed as arguments will automatically be prefixed with dots if
31 they are not present.
32
33 Methods can be shortened to their closest unambiguous forms, e.g. a, d, or l.
34 However, due to implementation specifics, any string starting with those forms
35 will work; that functionality is unspecified and is not guaranteed.
36
37 For example, the following are equivalent, with the first being preferred:
38     $prog add syslinux .virt
39     $prog a syslinux virt
40     $prog abcdefg syslinux virt
41
42 "
43 }
44
45 prepend_dot() {
46     case $1 in
47         .*) printf "$1";;
48         *)  printf ".$1";;
49     esac
50 }
51
52 show_reqs() {
53     _pkg="$1"
54     shift >/dev/null
55     [ -z "$_pkg" ] && return 1
56     if [ $# -gt 0 ]; then
57         # override reqs list for apkv add
58         _list=$(echo "$(for x do echo $x; done)" | sort | uniq)
59     else
60         apk info -e "$_pkg" >/dev/null || return 1
61         _list=$(apk info -R "$_pkg" | awk 'NR > 1 && NF' | sort)
62     fi
63     echo -e "\e[1m$_pkg\e[0m:" $_list
64 }
65
66 method="$1"
67 shift >/dev/null
68 case $method in
69     l*) method=list;;
70     a*) method=add;;
71     d*) method=del;;
72     h*) usage; exit;;
73     -*) usage; exit;;
74     *)  [ -n "$method" ] && echo "Invalid method $method" >&2; usage; exit 1;;
75 esac
76
77 if [ "$method" = list ]; then
78     if [ $# -eq 0 -o "$1" = '-o' ]; then
79         if [ "$1" = '-o' -a $# -gt 1 ]; then
80             echo "$method with -o switch does not accept arguments" >&2
81             usage
82             exit 1
83         fi
84         # ignore getopt's --, we don't care if an arg begins with - anyway
85         [ "$1" = '--' ] && switch >/dev/null
86
87         # OPTIMIZE: getting deps of virtual packages is slow, it would be
88         #           great to use apk info -R from show_reqs in orphan loop
89         all=$(apk info)
90         # show packages if no args
91         if [ $# -eq 0 ]; then
92             for x in $(echo "$all" | grep '^\.'); do
93                 show_reqs "$(prepend_dot "$x")"
94             done
95         fi
96         # set all child packages of virtual packages as owned
97         for v in $(echo "$all" | grep '^\.'); do
98             for c in $(apk info -R $v | tail +2 | grep -v '^\.'); do
99                 # NOTE: use tr because shell may not have ksh-derived ${c//}
100                 # XXX: package name may have illegal chars for variable name
101                 #      past those already replaced, and replacing chars may
102                 #      result in variable collision—but official apk repos do
103                 #      not have any packages that would cause issues
104                 eval "__$(echo $c | tr -s '.:-' _)=1"
105             done
106         done
107         # check world against owned packages
108         # TODO: notify user if package is in world and also a virtual package
109         orphans=$(
110             for x in $(cat /etc/apk/world | grep -v '^\.'); do
111                 eval "[ \${__$(
112                          echo $x | tr -s '.:-' _ | tr -s '<>=' "\n" | head -1
113                      )-0} -eq 0 ]" && echo $x
114             done
115         )
116         if [ -z "$orphans" ]; then
117             echo "No orphan packages found."
118         else
119             show_reqs orphans $orphans
120         fi
121     else
122         for x in $*; do
123             show_reqs "$(prepend_dot "$x")"
124         done
125     fi
126     exit
127 fi
128
129 ## add/del
130 # get and validate packages
131 pkgs=
132 virt=
133 for x; do
134     if [ -n "$pkgs" ]; then
135         pkgs="$pkgs $virt"
136     elif [ -n "$virt" ]; then
137         pkgs=$virt
138     fi
139     virt=$x
140 done
141 if [ -z "$pkgs" ]; then
142     echo 'Not enough arguments' >&2
143     usage
144     exit 1
145 fi
146 virt=$(prepend_dot $virt)
147 # exit if database locked (99) or no package found to delete from (1)
148 apk info -e "$virt" >/dev/null
149 code=$?
150 if [ $code -eq 99 -o \( $method = del -a $code -ne 0 \) ]; then
151     exit $code
152 fi
153
154 # we have validated our args, so let's prepare to run apk: get sudo command if
155 # it exists and we are not already root
156 # TODO: support doas
157 if [ "$(id -u)" != 0 ] && command -v sudo >/dev/null 2>&1; then
158     sudo=$(command -v sudo)
159 fi
160
161 # handle quick delete shorthand mentioned in help
162 if [ $method = del -a '*' = "$pkgs" ]; then
163     $sudo apk del "$virt"
164     exit $?
165 fi
166
167 currpkgs=$(apk info -R "$virt" | awk 'NR > 1 && NF')
168 case $method in
169     # NOTE: $(echo) construct uses word splitting to remove newlines
170     add) currpkgs="$(echo $currpkgs) $pkgs";;
171     del) # XXX: there's probably a more efficient way to do this
172          for x in $pkgs; do
173              currpkgs=$(echo "$currpkgs" | grep -vFiw $x)
174          done;;
175 esac
176
177 if [ -z "$currpkgs" ]; then
178     # virtual package is empty
179     $sudo apk del "$virt"
180     code=$?
181 else
182     show_reqs "$virt" $currpkgs
183     $sudo apk add -t "$virt" $currpkgs
184     code=$?
185     if [ $method = add -a $code -eq 0 ]; then
186         # remove packages from world if they are being moved to a virtual one
187         del=
188         for x in $pkgs; do
189             if apk info -e "$x" >/dev/null 2>&1; then
190                 del="$del $x"
191             fi
192         done
193         if [ -n "$del" ]; then
194             echo "Removing packages from world:$del"
195             $sudo apk del $del
196         fi
197     fi
198 fi
199 # pass along sudo/apk exit code
200 exit $code
201
202 # vi:sw=4:sts=4:et
This page took 0.044929 seconds and 4 git commands to generate.