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