vappman 0.4__py3-none-any.whl → 0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vappman/main.py +79 -42
- {vappman-0.4.dist-info → vappman-0.6.dist-info}/METADATA +33 -10
- vappman-0.6.dist-info/RECORD +9 -0
- vappman-0.4.dist-info/RECORD +0 -9
- {vappman-0.4.dist-info → vappman-0.6.dist-info}/LICENSE +0 -0
- {vappman-0.4.dist-info → vappman-0.6.dist-info}/WHEEL +0 -0
- {vappman-0.4.dist-info → vappman-0.6.dist-info}/entry_points.txt +0 -0
- {vappman-0.4.dist-info → vappman-0.6.dist-info}/top_level.txt +0 -0
vappman/main.py
CHANGED
|
@@ -14,6 +14,7 @@ Interactive, visual thin layer atop appman
|
|
|
14
14
|
import os
|
|
15
15
|
import sys
|
|
16
16
|
import re
|
|
17
|
+
import shutil
|
|
17
18
|
import subprocess
|
|
18
19
|
import traceback
|
|
19
20
|
import copy
|
|
@@ -32,27 +33,42 @@ class Vappman:
|
|
|
32
33
|
assert not Vappman.singleton
|
|
33
34
|
Vappman.singleton = self
|
|
34
35
|
|
|
35
|
-
self.summary_lines = []
|
|
36
|
-
self.new_summary_lines = True
|
|
37
|
-
|
|
38
36
|
spin = self.spin = OptionSpinner()
|
|
39
37
|
spin.add_key('help_mode', '? - toggle help screen', vals=[False, True])
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
# EXPAND
|
|
40
|
+
other = 'airbou/qscU'
|
|
42
41
|
other_keys = set(ord(x) for x in other)
|
|
42
|
+
other_keys.add(cs.KEY_ENTER)
|
|
43
|
+
other_keys.add(10) # another form of ENTER
|
|
43
44
|
self.opts = spin.default_obj
|
|
44
45
|
|
|
45
46
|
self.actions = {} # currently available actions
|
|
46
47
|
self.pick_app = '' # current picked app
|
|
47
48
|
self.pick_is_installed = False
|
|
48
|
-
# self.verbs = {'i': 'install', 'r': 'remove', 'v': 'view',}
|
|
49
49
|
self.prev_filter = '' # string
|
|
50
50
|
self.filter = None # compiled pattern
|
|
51
|
+
self.check_preqreqs()
|
|
51
52
|
self.apps = self.cmd_dict('appman list')
|
|
52
53
|
self.installs = self.get_installed() # dict keyed by app
|
|
53
54
|
self.win = Window(head_line=True, body_rows=len(self.apps)+20, head_rows=10,
|
|
54
55
|
keys=spin.keys ^ other_keys, mod_pick=self.mod_pick)
|
|
55
56
|
|
|
57
|
+
@staticmethod
|
|
58
|
+
def check_preqreqs():
|
|
59
|
+
""" Check that needed programs are installed. """
|
|
60
|
+
ok = True
|
|
61
|
+
for prog in 'curl grep jq sed wget appman'.split():
|
|
62
|
+
if shutil.which(prog) is None:
|
|
63
|
+
ok = False
|
|
64
|
+
print(f'ERROR: cannot find {prog!r} on $PATH')
|
|
65
|
+
if prog == 'appman':
|
|
66
|
+
print('Install appman with:')
|
|
67
|
+
print("""mkdir -p ~/.local/bin && cd /tmp &&"""
|
|
68
|
+
""" wget https://raw.githubusercontent.com/ivan-hc/AM/main/APP-MANAGER"""
|
|
69
|
+
""" -O appman && chmod a+x ./appman && mv ./appman ~/.local/bin/appman""")
|
|
70
|
+
if not ok:
|
|
71
|
+
sys.exit(1)
|
|
56
72
|
|
|
57
73
|
def cmd_dict(self, cmd, start='◆ '):
|
|
58
74
|
""" Get lines with the given start."""
|
|
@@ -77,7 +93,7 @@ class Vappman:
|
|
|
77
93
|
|
|
78
94
|
def get_installed(self):
|
|
79
95
|
""" Get the list of lines of installed apps """
|
|
80
|
-
rv = self.cmd_dict('appman files --
|
|
96
|
+
rv = self.cmd_dict('appman files --byname')
|
|
81
97
|
return rv
|
|
82
98
|
|
|
83
99
|
def main_loop(self):
|
|
@@ -89,13 +105,23 @@ class Vappman:
|
|
|
89
105
|
self.win.set_pick_mode(False)
|
|
90
106
|
self.spin.show_help_nav_keys(self.win)
|
|
91
107
|
self.spin.show_help_body(self.win)
|
|
108
|
+
# EXPAND
|
|
92
109
|
lines = [
|
|
93
110
|
'ALWAYS AVAILABLE:',
|
|
94
|
-
' / - filter apps',
|
|
95
111
|
' q - quit program (CTL-C disabled)',
|
|
112
|
+
' a - about (more info about app)',
|
|
113
|
+
' s - sync (update appman itself)',
|
|
114
|
+
' c - clean (remove unneeded files/folters)',
|
|
115
|
+
' U - update ALL installed apps',
|
|
116
|
+
' / - filter apps',
|
|
117
|
+
' ENTER = install, remove, or return from help',
|
|
96
118
|
'CONTEXT SENSITIVE:',
|
|
97
119
|
' i - install uninstalled app',
|
|
98
120
|
' r - remove installed app',
|
|
121
|
+
' b - backup installed app',
|
|
122
|
+
' u - update installed app',
|
|
123
|
+
' o - overwrite app from its backup',
|
|
124
|
+
|
|
99
125
|
]
|
|
100
126
|
for line in lines:
|
|
101
127
|
self.win.put_body(line)
|
|
@@ -106,15 +132,15 @@ class Vappman:
|
|
|
106
132
|
# self.win.set_pick_mode(self.opts.pick_mode, self.opts.pick_size)
|
|
107
133
|
self.win.set_pick_mode(True)
|
|
108
134
|
self.win.add_header(self.get_keys_line(), attr=cs.A_BOLD)
|
|
109
|
-
for line in self.installs.
|
|
135
|
+
for app, line in self.installs.items():
|
|
136
|
+
if app in self.apps:
|
|
137
|
+
line = self.apps[app]
|
|
110
138
|
if wanted(line):
|
|
139
|
+
line = '✔✔✔' + line[1:]
|
|
111
140
|
self.win.add_body(line)
|
|
112
141
|
for app, line in self.apps.items():
|
|
113
142
|
if app not in self.installs and wanted(line):
|
|
114
143
|
self.win.add_body(line)
|
|
115
|
-
# if self.new_summary_lines:
|
|
116
|
-
# self.win.pick_pos = self.win.scroll_pos = 0
|
|
117
|
-
# self.new_summary_lines = False
|
|
118
144
|
self.win.render()
|
|
119
145
|
|
|
120
146
|
_ = self.do_key(self.win.prompt(seconds=300))
|
|
@@ -122,11 +148,13 @@ class Vappman:
|
|
|
122
148
|
|
|
123
149
|
def get_keys_line(self):
|
|
124
150
|
""" TBD """
|
|
151
|
+
# EXPAND
|
|
125
152
|
filt = self.prev_filter if self.prev_filter else '{No-Filt}'
|
|
126
153
|
line = 'KEYS:'
|
|
127
154
|
for key, verb in self.actions.items():
|
|
128
155
|
line += f' {key}:{verb}'
|
|
129
|
-
|
|
156
|
+
# or EXPAND
|
|
157
|
+
line += f' ?:help q:quit a:about s:sync c:clean U:upd /{filt} '
|
|
130
158
|
# for action in self.actions:
|
|
131
159
|
# line += f' {action[0]}:{action}'
|
|
132
160
|
return line
|
|
@@ -139,8 +167,12 @@ class Vappman:
|
|
|
139
167
|
line = lines[self.win.pick_pos]
|
|
140
168
|
app = self.get_word1(line)
|
|
141
169
|
self.pick_is_installed = bool(app in self.installs)
|
|
170
|
+
# EXPAND
|
|
142
171
|
if self.pick_is_installed:
|
|
143
|
-
actions['r'] = '
|
|
172
|
+
actions['r'] = 'rmv'
|
|
173
|
+
actions['b'] = 'bkup'
|
|
174
|
+
actions['o'] = 'overwr'
|
|
175
|
+
actions['u'] = 'upd'
|
|
144
176
|
else:
|
|
145
177
|
actions['i'] = 'install'
|
|
146
178
|
|
|
@@ -170,54 +202,59 @@ class Vappman:
|
|
|
170
202
|
# line = line[0:-over]
|
|
171
203
|
|
|
172
204
|
# return line + suffix
|
|
205
|
+
def run_appman(self, cmd):
|
|
206
|
+
Window.stop_curses()
|
|
207
|
+
os.system(f'clear; stty sane; {cmd};'
|
|
208
|
+
+ ' /bin/echo -e "\n\n===== Press ENTER for menu ====> \c"; read FOO')
|
|
209
|
+
self.installs = self.get_installed()
|
|
210
|
+
Window._start_curses()
|
|
173
211
|
|
|
174
212
|
def do_key(self, key):
|
|
175
213
|
""" TBD """
|
|
176
|
-
def dashes(ns, ok=True):
|
|
177
|
-
try:
|
|
178
|
-
# use checkmark or circle-backslash
|
|
179
|
-
filler = ('\u2713' if ok else '\u2342') * (ns.end - ns.start)
|
|
180
|
-
line = self.summary_lines[self.win.pick_pos]
|
|
181
|
-
line = line[0:ns.start] + filler + line[ns.end:]
|
|
182
|
-
self.summary_lines[self.win.pick_pos] = line
|
|
183
|
-
self.win.draw(self.win.pick_pos, 0, line, width=self.win.get_pad_width(), header=False)
|
|
184
|
-
self.win.fix_positions(delta=1)
|
|
185
|
-
self.win.render()
|
|
186
|
-
except Exception:
|
|
187
|
-
pass
|
|
188
|
-
|
|
189
214
|
if not key:
|
|
190
215
|
return True
|
|
216
|
+
if key == cs.KEY_ENTER or key == 10: # Handle ENTER
|
|
217
|
+
if self.opts.help_mode:
|
|
218
|
+
self.opts.help_mode = False
|
|
219
|
+
return True
|
|
220
|
+
elif self.pick_is_installed:
|
|
221
|
+
key = ord('r') # removed installed app
|
|
222
|
+
else:
|
|
223
|
+
key = ord('i') # install uninstalled app
|
|
224
|
+
|
|
191
225
|
if key in self.spin.keys:
|
|
192
226
|
value = self.spin.do_key(key, self.win)
|
|
193
|
-
if key in (ord('p'), ord('s')):
|
|
194
|
-
self.win.set_pick_mode(on=self.opts.pick_mode, pick_size=self.opts.pick_size)
|
|
195
|
-
elif key == ord('n'):
|
|
196
|
-
self.win.alert(title='Info', message=f'got: {value}')
|
|
197
227
|
return value
|
|
228
|
+
|
|
198
229
|
if key == ord('q'):
|
|
199
230
|
self.win.stop_curses()
|
|
200
231
|
os.system('clear; stty sane')
|
|
201
232
|
sys.exit(0)
|
|
202
233
|
|
|
203
234
|
if key == ord('i') and not self.pick_is_installed:
|
|
204
|
-
|
|
205
|
-
Window.stop_curses()
|
|
206
|
-
os.system(f'clear; stty sane; appman install {self.pick_app};'
|
|
207
|
-
+ ' echo -e "\n\nHit ENTER to return to menu"; read FOO')
|
|
208
|
-
self.installs = self.get_installed()
|
|
209
|
-
Window._start_curses()
|
|
235
|
+
self.run_appman(f'appman install {self.pick_app}')
|
|
210
236
|
return None
|
|
211
237
|
|
|
212
238
|
if key == ord('r') and self.pick_is_installed:
|
|
213
|
-
|
|
214
|
-
Window.stop_curses()
|
|
215
|
-
os.system(f'clear; stty sane; appman remove {self.pick_app};'
|
|
216
|
-
+ ' echo -e "\n\nHit ENTER to return to menu"; read FOO')
|
|
217
|
-
self.installs = self.get_installed() # dict keyed by app
|
|
218
|
-
Window._start_curses()
|
|
239
|
+
self.run_appman(f'appman remove {self.pick_app}')
|
|
219
240
|
return None
|
|
220
241
|
|
|
242
|
+
if key == ord('s'):
|
|
243
|
+
self.run_appman('appman sync')
|
|
244
|
+
if key == ord('c'):
|
|
245
|
+
self.run_appman('appman clean')
|
|
246
|
+
if key == ord('b'):
|
|
247
|
+
self.run_appman(f'appman backup {self.pick_app}')
|
|
248
|
+
if key == ord('o'):
|
|
249
|
+
self.run_appman(f'appman overwrite {self.pick_app}')
|
|
250
|
+
if key == ord('a'):
|
|
251
|
+
self.run_appman(f'appman about {self.pick_app}')
|
|
252
|
+
if key == ord('u'):
|
|
253
|
+
self.run_appman(f'appman update {self.pick_app}')
|
|
254
|
+
if key == ord('U'):
|
|
255
|
+
self.run_appman(f'appman update')
|
|
256
|
+
# EXPAND
|
|
257
|
+
|
|
221
258
|
if key == ord('/'):
|
|
222
259
|
# pylint: disable=protected-access
|
|
223
260
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vappman
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6
|
|
4
4
|
Summary: A visual wrapper for appman
|
|
5
5
|
Author-email: Joe Defen <joedef@google.com>
|
|
6
6
|
License: MIT
|
|
@@ -22,20 +22,41 @@ Requires-Dist: importlib-metadata ; python_version < "3.8"
|
|
|
22
22
|
* Install `vappman` using `pipx install vappman`, or however you do so.
|
|
23
23
|
* Prerequisites: install [ivan-hc/AppMan: AppImage package manager to install, update (for real) and manage ALL of them locally thanks to "AM", the ever-growing AUR-inspired database listing (for now) 1900+ portable apps and programs for GNU/Linux. Manage your AppImages with the ease of APT and the power of PacMan.](https://github.com/ivan-hc/AppMan) and all of its prerequisites.
|
|
24
24
|
|
|
25
|
-
NOTE: `vappman`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* installing
|
|
29
|
-
* removing installed apps
|
|
25
|
+
NOTE: `vappman` covers many capabilities of appman:
|
|
26
|
+
* implicitly, (-f) files (or show installed), (-l) list available apps,
|
|
27
|
+
and (-q) search the app list
|
|
28
|
+
* (-i) installing uninstalled apps
|
|
29
|
+
* (-r) removing installed apps
|
|
30
|
+
* (-b) backup / (-o) overwrite of installed apps
|
|
31
|
+
* (-a) about (i.e., more info) for all apps
|
|
32
|
+
* (-c) clean to remove unneeded files and directories
|
|
33
|
+
* (-u) update installed apps; and `vappman` uses "U" for update
|
|
34
|
+
all installed apps
|
|
35
|
+
|
|
36
|
+
But it does NOT cover:
|
|
37
|
+
* (-d) download install script
|
|
38
|
+
* (-h) help or full help for appman
|
|
39
|
+
* (-H) home or set $HOME directory for apps
|
|
40
|
+
* (-t) template for custom install template
|
|
41
|
+
* (-v) version of appman
|
|
42
|
+
* --force-latest to get the most recent stable release AND
|
|
43
|
+
all other options and unmentioned commands.
|
|
30
44
|
|
|
31
45
|
## Usage
|
|
32
46
|
* Run `vappman` from the command line.
|
|
33
47
|
* It presents some keys available on the top line.
|
|
34
48
|
* Use '?' to learn the navigation keys (e.g., you can use the mouse wheel,
|
|
35
49
|
arrow keys, and many `vi`-like keys)
|
|
50
|
+
* '?' also elaborates the meaning of the available keys for operations.
|
|
51
|
+
* NOTE: `ENTER` acts differently based on context:
|
|
52
|
+
* In help, it returns to the main menu.
|
|
53
|
+
* On an uninstalled app, it installs it.
|
|
54
|
+
* On an installed app, it uninstalls it.
|
|
36
55
|
* Then `vappman` presents a list of installed apps, followed by available/uninstalled apps.
|
|
56
|
+
* Installed apps have prefix '✔✔✔' (i.e., three checks).
|
|
57
|
+
* Uninstalled apps have prefix '◆' (i.e., a solid diamond).
|
|
37
58
|
* Enter `/` to enter a "filter" for installed/uninstalled apps, if you wish.
|
|
38
|
-
* If you enter plain
|
|
59
|
+
* If you enter plain ole "words", then those words must match the beginning of words
|
|
39
60
|
of the apps or descriptions (in order, but not contiguously).
|
|
40
61
|
* Or you can enter an regular expression acceptable to python (e.g., `\b` means word
|
|
41
62
|
boundary, etc.)
|
|
@@ -46,10 +67,12 @@ but it implements the most needed, basic functionality:
|
|
|
46
67
|
|
|
47
68
|
---
|
|
48
69
|
|
|
49
|
-
NOTES:
|
|
50
|
-
* the filter is `card` so it shows
|
|
51
|
-
* the current position is on `glabels`;
|
|
70
|
+
NOTES: in this example:
|
|
71
|
+
* the filter is `card` so it shows app lines with words starting with `card`.
|
|
72
|
+
* the reverse video, current position is on `glabels`;
|
|
73
|
+
thus if `i` (or ENTER) is typed, `appman install glabels` is run.
|
|
52
74
|
* if the horizontal line (second line show) has no decorations, then you are looking
|
|
53
75
|
all the filtered apps; otherwise, the decoration suggests where you are in the
|
|
54
76
|
partial view of the filtered apps.
|
|
77
|
+
* the matching installed app has the '✔✔✔' prefix.
|
|
55
78
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
vappman/PowerWindow.py,sha256=OLCX-RkbJZ2wwaY7M-4Eo9PQuR95TWrGNIY8DdVRpsE,28567
|
|
2
|
+
vappman/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
vappman/main.py,sha256=rSWpH_l_IgH3jR4HSG-H8lvmuuAZWStPo-AMtSIsZYY,10483
|
|
4
|
+
vappman-0.6.dist-info/LICENSE,sha256=qB9OdnyyF6WYHiEIXVm0rOSdcf8e2ctorrtWs6CC5lU,1062
|
|
5
|
+
vappman-0.6.dist-info/METADATA,sha256=pWiQZo-R_ePgEX3Yz9oAcNty2XBmYCArV--3ErQd_HI,3850
|
|
6
|
+
vappman-0.6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
7
|
+
vappman-0.6.dist-info/entry_points.txt,sha256=7_1MiUvkCJoElLePOCJYqhkQN4xmadBRQCKupOwzt90,46
|
|
8
|
+
vappman-0.6.dist-info/top_level.txt,sha256=5_Gb5oZh7s2-i62gLXZ6INVALAV9D0-yqh0TvNqpPC4,8
|
|
9
|
+
vappman-0.6.dist-info/RECORD,,
|
vappman-0.4.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
vappman/PowerWindow.py,sha256=OLCX-RkbJZ2wwaY7M-4Eo9PQuR95TWrGNIY8DdVRpsE,28567
|
|
2
|
-
vappman/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
vappman/main.py,sha256=AhLwXlidafcrB-g5-6OONwaLW_JhAxCSsZq8derbZ_8,9221
|
|
4
|
-
vappman-0.4.dist-info/LICENSE,sha256=qB9OdnyyF6WYHiEIXVm0rOSdcf8e2ctorrtWs6CC5lU,1062
|
|
5
|
-
vappman-0.4.dist-info/METADATA,sha256=oTTGUtaApTjDeloLWTJyTmDobEAoqNNikJB7c3_nPRk,2749
|
|
6
|
-
vappman-0.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
7
|
-
vappman-0.4.dist-info/entry_points.txt,sha256=7_1MiUvkCJoElLePOCJYqhkQN4xmadBRQCKupOwzt90,46
|
|
8
|
-
vappman-0.4.dist-info/top_level.txt,sha256=5_Gb5oZh7s2-i62gLXZ6INVALAV9D0-yqh0TvNqpPC4,8
|
|
9
|
-
vappman-0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|