]>
Commit | Line | Data |
---|---|---|
c0b3d4b6 | 1 | #!/bin/sh |
de4637f9 | 2 | # TODO: add duplicate checker to seeif 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 |
de4637f9 | 16 | packages are removed from world. The shorthand '$prog del * <virtual>' can |
c0b3d4b6 | 17 | be used to empty <virtual>. |
de4637f9 | 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 | |
c0b3d4b6 | 27 | Show this help. |
28 | ||
de4637f9 | 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. | |
c0b3d4b6 | 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 | ||
de4637f9 | 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 | |
c0b3d4b6 | 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 | |
de4637f9 | 57 | # override reqs list for apkv add |
c0b3d4b6 | 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 | |
de4637f9 | 69 | l*) method=list;; |
c0b3d4b6 | 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 | ||
de4637f9 | 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 | ||
de4637f9 | 87 | all=$(apk info) |
88 | # show packages if no args | |
89 | if [ $# -eq 0 ]; then | |
90 | for x in $(echo "$all" | grep '^\.'); do | |
91 | show_reqs "$(prepend_dot "$x")" | |
92 | done | |
93 | fi | |
94 | # set all child packages of virtual packages as owned | |
4974d671 | 95 | # OPTIMIZE: getting deps of virtual packages is slow, it would be |
96 | # great to use apk info -R from show_reqs in orphan loop | |
97 | # NOTE: since we don't have arrays in sh, set individual vars instead | |
de4637f9 | 98 | for v in $(echo "$all" | grep '^\.'); do |
99 | for c in $(apk info -R $v | tail +2 | grep -v '^\.'); do | |
100 | # NOTE: use tr because shell may not have ksh-derived ${c//} | |
4974d671 | 101 | # XXX: have not verified if package name may have more illegal |
102 | # chars for variable name past those already replaced, and | |
103 | # replacing chars may result in variable collision—but | |
104 | # official apk repos do not have any package names that | |
105 | # would cause issues | |
de4637f9 | 106 | eval "__$(echo $c | tr -s '.:-' _)=1" |
107 | done | |
108 | done | |
109 | # check world against owned packages | |
4974d671 | 110 | # TODO: notify user if package is both in world and virtual package |
de4637f9 | 111 | orphans=$( |
112 | for x in $(cat /etc/apk/world | grep -v '^\.'); do | |
113 | eval "[ \${__$( | |
4974d671 | 114 | echo $x | tr -s '.:-' _ | tr -s '<=~>@' "\n" | head -1 |
de4637f9 | 115 | )-0} -eq 0 ]" && echo $x |
116 | done | |
117 | ) | |
118 | if [ -z "$orphans" ]; then | |
119 | echo "No orphan packages found." | |
120 | else | |
121 | show_reqs orphans $orphans | |
122 | fi | |
123 | else | |
124 | for x in $*; do | |
125 | show_reqs "$(prepend_dot "$x")" | |
126 | done | |
127 | fi | |
128 | exit | |
129 | fi | |
130 | ||
c0b3d4b6 | 131 | ## add/del |
132 | # get and validate packages | |
133 | pkgs= | |
134 | virt= | |
135 | for x; do | |
136 | if [ -n "$pkgs" ]; then | |
137 | pkgs="$pkgs $virt" | |
138 | elif [ -n "$virt" ]; then | |
139 | pkgs=$virt | |
140 | fi | |
141 | virt=$x | |
142 | done | |
143 | if [ -z "$pkgs" ]; then | |
144 | echo 'Not enough arguments' >&2 | |
145 | usage | |
146 | exit 1 | |
147 | fi | |
148 | virt=$(prepend_dot $virt) | |
149 | # exit if database locked (99) or no package found to delete from (1) | |
150 | apk info -e "$virt" >/dev/null | |
151 | code=$? | |
152 | if [ $code -eq 99 -o \( $method = del -a $code -ne 0 \) ]; then | |
153 | exit $code | |
154 | fi | |
155 | ||
156 | # we have validated our args, so let's prepare to run apk: get sudo command if | |
157 | # it exists and we are not already root | |
158 | # TODO: support doas | |
159 | if [ "$(id -u)" != 0 ] && command -v sudo >/dev/null 2>&1; then | |
160 | sudo=$(command -v sudo) | |
161 | fi | |
162 | ||
163 | # handle quick delete shorthand mentioned in help | |
164 | if [ $method = del -a '*' = "$pkgs" ]; then | |
165 | $sudo apk del "$virt" | |
166 | exit $? | |
167 | fi | |
168 | ||
169 | currpkgs=$(apk info -R "$virt" | awk 'NR > 1 && NF') | |
170 | case $method in | |
de4637f9 | 171 | # NOTE: $(echo) construct uses word splitting to remove newlines |
c0b3d4b6 | 172 | add) currpkgs="$(echo $currpkgs) $pkgs";; |
173 | del) # XXX: there's probably a more efficient way to do this | |
bf0563c2 | 174 | for x in $pkgs; do |
c0b3d4b6 | 175 | currpkgs=$(echo "$currpkgs" | grep -vFiw $x) |
176 | done;; | |
177 | esac | |
178 | ||
179 | if [ -z "$currpkgs" ]; then | |
180 | # virtual package is empty | |
181 | $sudo apk del "$virt" | |
de4637f9 | 182 | code=$? |
c0b3d4b6 | 183 | else |
184 | show_reqs "$virt" $currpkgs | |
185 | $sudo apk add -t "$virt" $currpkgs | |
de4637f9 | 186 | code=$? |
187 | if [ $method = add -a $code -eq 0 ]; then | |
188 | # remove packages from world if they are being moved to a virtual one | |
189 | del= | |
190 | for x in $pkgs; do | |
4974d671 | 191 | #apk_adb.c: [!]name[<,<=,<~,=,~,>~,>=,>,><]ver, @ for pinning |
192 | if grep -qE "^$x($|[<=~>@])" /etc/apk/world; then | |
de4637f9 | 193 | del="$del $x" |
194 | fi | |
195 | done | |
196 | if [ -n "$del" ]; then | |
197 | echo "Removing packages from world:$del" | |
198 | $sudo apk del $del | |
199 | fi | |
200 | fi | |
c0b3d4b6 | 201 | fi |
202 | # pass along sudo/apk exit code | |
de4637f9 | 203 | exit $code |
c0b3d4b6 | 204 | |
205 | # vi:sw=4:sts=4:et |