vappman 0.7__tar.gz → 0.8__tar.gz
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-0.7/src/vappman.egg-info → vappman-0.8}/PKG-INFO +6 -3
- {vappman-0.7 → vappman-0.8}/README.md +5 -2
- {vappman-0.7 → vappman-0.8}/pyproject.toml +1 -1
- {vappman-0.7 → vappman-0.8}/src/vappman/PowerWindow.py +1 -1
- {vappman-0.7 → vappman-0.8}/src/vappman/main.py +149 -7
- {vappman-0.7 → vappman-0.8/src/vappman.egg-info}/PKG-INFO +6 -3
- {vappman-0.7 → vappman-0.8}/LICENSE +0 -0
- {vappman-0.7 → vappman-0.8}/setup.cfg +0 -0
- {vappman-0.7 → vappman-0.8}/src/vappman/__init__.py +0 -0
- {vappman-0.7 → vappman-0.8}/src/vappman.egg-info/SOURCES.txt +0 -0
- {vappman-0.7 → vappman-0.8}/src/vappman.egg-info/dependency_links.txt +0 -0
- {vappman-0.7 → vappman-0.8}/src/vappman.egg-info/entry_points.txt +0 -0
- {vappman-0.7 → vappman-0.8}/src/vappman.egg-info/requires.txt +0 -0
- {vappman-0.7 → vappman-0.8}/src/vappman.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vappman
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8
|
|
4
4
|
Summary: A visual wrapper for appman
|
|
5
5
|
Author-email: Joe Defen <joedef@google.com>
|
|
6
6
|
License: MIT
|
|
@@ -41,7 +41,7 @@ But it does NOT cover:
|
|
|
41
41
|
* (-v) version of appman
|
|
42
42
|
* --force-latest to get the most recent stable release AND
|
|
43
43
|
all other options and unmentioned commands.
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
## Usage
|
|
46
46
|
* Run `vappman` from the command line.
|
|
47
47
|
* It presents some keys available on the top line.
|
|
@@ -67,8 +67,9 @@ But it does NOT cover:
|
|
|
67
67
|
* `ESC` clears the filter and jumps to the top of the listing.
|
|
68
68
|
* Each time the filter is changed, the position jumps to the top of the listing.
|
|
69
69
|
* Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
|
|
70
|
+
* Use `t` to "test" an installed app. This launches a terminal emulator and then the app so you can see issues. This is not for daily use obviously, but for after install or when having unknown issues and you wish to start the investigation.
|
|
70
71
|
|
|
71
|
-
## Example Screenshot
|
|
72
|
+
## Example Screenshot (of v0.7 ... current release will vary slightly)
|
|
72
73
|
.
|
|
73
74
|
|
|
74
75
|
---
|
|
@@ -84,3 +85,5 @@ NOTES: in this example:
|
|
|
84
85
|
* the fixed top line shows some of the available action keys (e.g., `q` quits the app)
|
|
85
86
|
* use `?` to open the help screen describing all keys (including navigation)
|
|
86
87
|
|
|
88
|
+
## Screen Recording (Intro to vappman based on v0.7)
|
|
89
|
+
[](https://youtu.be/NUHYN9_DZtA)
|
|
@@ -23,7 +23,7 @@ But it does NOT cover:
|
|
|
23
23
|
* (-v) version of appman
|
|
24
24
|
* --force-latest to get the most recent stable release AND
|
|
25
25
|
all other options and unmentioned commands.
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
## Usage
|
|
28
28
|
* Run `vappman` from the command line.
|
|
29
29
|
* It presents some keys available on the top line.
|
|
@@ -49,8 +49,9 @@ But it does NOT cover:
|
|
|
49
49
|
* `ESC` clears the filter and jumps to the top of the listing.
|
|
50
50
|
* Each time the filter is changed, the position jumps to the top of the listing.
|
|
51
51
|
* Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
|
|
52
|
+
* Use `t` to "test" an installed app. This launches a terminal emulator and then the app so you can see issues. This is not for daily use obviously, but for after install or when having unknown issues and you wish to start the investigation.
|
|
52
53
|
|
|
53
|
-
## Example Screenshot
|
|
54
|
+
## Example Screenshot (of v0.7 ... current release will vary slightly)
|
|
54
55
|
.
|
|
55
56
|
|
|
56
57
|
---
|
|
@@ -66,3 +67,5 @@ NOTES: in this example:
|
|
|
66
67
|
* the fixed top line shows some of the available action keys (e.g., `q` quits the app)
|
|
67
68
|
* use `?` to open the help screen describing all keys (including navigation)
|
|
68
69
|
|
|
70
|
+
## Screen Recording (Intro to vappman based on v0.7)
|
|
71
|
+
[](https://youtu.be/NUHYN9_DZtA)
|
|
@@ -588,7 +588,7 @@ class Window:
|
|
|
588
588
|
def alert(self, title='ALERT', message='', height=1, width=80):
|
|
589
589
|
"""Alert box"""
|
|
590
590
|
def mod_key(key):
|
|
591
|
-
return 7 if key
|
|
591
|
+
return 7 if key in (10, curses.KEY_ENTER) else key
|
|
592
592
|
|
|
593
593
|
# need 3 extra cols for rectangle (so we don't draw in southeast corner)
|
|
594
594
|
# and 3 rows (top/prompt/bottom)
|
|
@@ -6,7 +6,7 @@ Interactive, visual thin layer atop appman
|
|
|
6
6
|
# pylint: disable=broad-exception-caught,consider-using-with
|
|
7
7
|
# pylint: disable=too-many-instance-attributes,too-many-branches
|
|
8
8
|
# pylint: disable=too-many-return-statements,too-many-statements
|
|
9
|
-
# pylint: disable=consider-using-in
|
|
9
|
+
# pylint: disable=consider-using-in,too-many-nested-blocks
|
|
10
10
|
# pylint: disable=wrong-import-position,disable=wrong-import-order
|
|
11
11
|
# import VirtEnv
|
|
12
12
|
# VirtEnv.ensure_venv(__name__)
|
|
@@ -14,6 +14,7 @@ Interactive, visual thin layer atop appman
|
|
|
14
14
|
import os
|
|
15
15
|
import sys
|
|
16
16
|
import re
|
|
17
|
+
import glob
|
|
17
18
|
import shutil
|
|
18
19
|
import subprocess
|
|
19
20
|
import traceback
|
|
@@ -34,7 +35,7 @@ class Vappman:
|
|
|
34
35
|
spin.add_key('help_mode', '? - toggle help screen', vals=[False, True])
|
|
35
36
|
|
|
36
37
|
# EXPAND
|
|
37
|
-
other = '
|
|
38
|
+
other = 'airtbou/qxscU'
|
|
38
39
|
other_keys = set(ord(x) for x in other)
|
|
39
40
|
other_keys.add(cs.KEY_ENTER)
|
|
40
41
|
other_keys.add(27) # ESCAPE
|
|
@@ -49,6 +50,9 @@ class Vappman:
|
|
|
49
50
|
self.check_preqreqs()
|
|
50
51
|
self.apps = self.cmd_dict('appman list')
|
|
51
52
|
self.installs = self.get_installed() # dict keyed by app
|
|
53
|
+
self.appman_dir = self.get_appman_dir()
|
|
54
|
+
self.dot_desktop_dir = self.get_dot_desktop_dir()
|
|
55
|
+
self.terminal_emulator = None
|
|
52
56
|
self.win = Window(head_line=True, body_rows=len(self.apps)+20, head_rows=10,
|
|
53
57
|
keys=spin.keys ^ other_keys, mod_pick=self.mod_pick)
|
|
54
58
|
|
|
@@ -94,6 +98,43 @@ class Vappman:
|
|
|
94
98
|
rv = self.cmd_dict('appman files --byname')
|
|
95
99
|
return rv
|
|
96
100
|
|
|
101
|
+
@staticmethod
|
|
102
|
+
def get_appman_dir():
|
|
103
|
+
""" Try to figure out where the apps are stored. """
|
|
104
|
+
|
|
105
|
+
appman_dir = None, None
|
|
106
|
+
try:
|
|
107
|
+
config_dir = os.getenv('XDG_CONFIG_HOME')
|
|
108
|
+
if not config_dir:
|
|
109
|
+
config_dir = os.path.join(os.getenv('HOME'), '.config')
|
|
110
|
+
config_file = os.path.join(config_dir, 'appman', 'appman-config')
|
|
111
|
+
with open(config_file, 'r', encoding='utf-8') as fh:
|
|
112
|
+
appman_dir = fh.read().strip()
|
|
113
|
+
appman_dir = os.path.join(os.getenv('HOME'), appman_dir)
|
|
114
|
+
os.listdir(appman_dir)
|
|
115
|
+
return appman_dir
|
|
116
|
+
except Exception as exc:
|
|
117
|
+
print(f'NOTE: cannot get appman dir; tried below {appman_dir!r}; {exc}')
|
|
118
|
+
print(' Check if contents of ~/config/appman/appman-config'
|
|
119
|
+
+ ' is the subdir of $HOME w your appman apps')
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def get_dot_desktop_dir():
|
|
124
|
+
""" Try to figure out where the .desktop files are stored. """
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
data_dir = os.getenv('XDG_DATA_HOME')
|
|
128
|
+
if not data_dir:
|
|
129
|
+
data_dir = os.path.join(os.getenv('HOME'), '.local', 'share')
|
|
130
|
+
dot_dir = os.path.join(data_dir, 'applications')
|
|
131
|
+
os.listdir(dot_dir)
|
|
132
|
+
return dot_dir
|
|
133
|
+
except Exception as exc:
|
|
134
|
+
print(f'NOTE: cannot get .desktop dir; tried below {data_dir!r}; {exc}')
|
|
135
|
+
print(' Check if contents of ~/.local/share/applications for .desktop files')
|
|
136
|
+
return None
|
|
137
|
+
|
|
97
138
|
def main_loop(self):
|
|
98
139
|
""" TBD """
|
|
99
140
|
|
|
@@ -106,7 +147,7 @@ class Vappman:
|
|
|
106
147
|
# EXPAND
|
|
107
148
|
lines = [
|
|
108
149
|
'ALWAYS AVAILABLE:',
|
|
109
|
-
' q - quit program (CTL-C disabled)',
|
|
150
|
+
' q or x - quit program (CTL-C disabled)',
|
|
110
151
|
' a - about (more info about app)',
|
|
111
152
|
' s - sync (update appman itself)',
|
|
112
153
|
' c - clean (remove unneeded files/folters)',
|
|
@@ -119,6 +160,7 @@ class Vappman:
|
|
|
119
160
|
' r - remove installed app',
|
|
120
161
|
' b - backup installed app',
|
|
121
162
|
' u - update installed app',
|
|
163
|
+
' t - test by opening a terminal emulator and launching the app'
|
|
122
164
|
' o - overwrite app from its backup',
|
|
123
165
|
|
|
124
166
|
]
|
|
@@ -127,6 +169,12 @@ class Vappman:
|
|
|
127
169
|
else:
|
|
128
170
|
def wanted(line):
|
|
129
171
|
return not self.filter or self.filter.search(line)
|
|
172
|
+
def version_of(app):
|
|
173
|
+
# ◆ krita | 5.2.2 | appimage-type2 | 355 MiB
|
|
174
|
+
fields = self.installs[app].split('|')
|
|
175
|
+
if len(fields) >= 2:
|
|
176
|
+
return fields[1].strip()
|
|
177
|
+
return '?version?'
|
|
130
178
|
|
|
131
179
|
# self.win.set_pick_mode(self.opts.pick_mode, self.opts.pick_size)
|
|
132
180
|
self.win.set_pick_mode(True)
|
|
@@ -135,7 +183,7 @@ class Vappman:
|
|
|
135
183
|
if app in self.apps:
|
|
136
184
|
line = self.apps[app]
|
|
137
185
|
if wanted(line[2:]):
|
|
138
|
-
line = '✔✔✔
|
|
186
|
+
line = f'✔✔✔ {app} [{version_of(app)}] :{line.split(':', maxsplit=1)[1]}'
|
|
139
187
|
self.win.add_body(line)
|
|
140
188
|
for app, line in self.apps.items():
|
|
141
189
|
if app not in self.installs and wanted(line[2:]):
|
|
@@ -174,6 +222,7 @@ class Vappman:
|
|
|
174
222
|
actions['b'] = 'bkup'
|
|
175
223
|
actions['o'] = 'overwr'
|
|
176
224
|
actions['u'] = 'upd'
|
|
225
|
+
actions['t'] = 'test'
|
|
177
226
|
else:
|
|
178
227
|
actions['i'] = 'install'
|
|
179
228
|
|
|
@@ -213,6 +262,95 @@ class Vappman:
|
|
|
213
262
|
self.installs = self.get_installed()
|
|
214
263
|
Window._start_curses()
|
|
215
264
|
|
|
265
|
+
@staticmethod
|
|
266
|
+
def launch_desktop_file(desktop_file_path):
|
|
267
|
+
""" Launch the .desktop file using xdg-open in a detached process """
|
|
268
|
+
try:
|
|
269
|
+
trial = ['xdg-open', desktop_file_path]
|
|
270
|
+
subprocess.Popen(trial,
|
|
271
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, close_fds=True)
|
|
272
|
+
return None
|
|
273
|
+
except Exception:
|
|
274
|
+
return trial
|
|
275
|
+
|
|
276
|
+
def launch_in_terminal(self, executable):
|
|
277
|
+
""" Find a terminal emulator"""
|
|
278
|
+
if not self.terminal_emulator:
|
|
279
|
+
maybes = [
|
|
280
|
+
[ 'konsole', '--noclose', '-e', '"{command}"'],
|
|
281
|
+
[ 'gnome-terminal', '--', 'bash', '-c', '"{command}"; exec bash' ],
|
|
282
|
+
[ 'xfce4-terminal', '--hold', '--command="{command}"' ],
|
|
283
|
+
[ 'lxterminal', '-e', """bash -c '"{command}"; echo; read -p "Press Enter to close..."'"""],
|
|
284
|
+
# [ terminator', ],
|
|
285
|
+
# [ alacritty', ],
|
|
286
|
+
# [ termite', ],
|
|
287
|
+
# [ urxvt', ],
|
|
288
|
+
# [ sakura', ],
|
|
289
|
+
# [ tilix', ],
|
|
290
|
+
# [ kitty', ],
|
|
291
|
+
# [ hyper', ],
|
|
292
|
+
# [ guake', ],
|
|
293
|
+
# [ yakuake', ],
|
|
294
|
+
]
|
|
295
|
+
for maybe in maybes:
|
|
296
|
+
if shutil.which(maybe[0]):
|
|
297
|
+
self.terminal_emulator = maybe
|
|
298
|
+
break
|
|
299
|
+
if self.terminal_emulator:
|
|
300
|
+
try:
|
|
301
|
+
trial = []
|
|
302
|
+
for wd in self.terminal_emulator:
|
|
303
|
+
trial.append(wd.replace('{command}', executable))
|
|
304
|
+
subprocess.Popen(trial)
|
|
305
|
+
return None
|
|
306
|
+
except Exception:
|
|
307
|
+
return trial
|
|
308
|
+
return trial
|
|
309
|
+
|
|
310
|
+
def launch_app(self, app):
|
|
311
|
+
""" Try to run an app."""
|
|
312
|
+
# First dig out where it might be installed as a .desktop file
|
|
313
|
+
# by searching the 'remove' script
|
|
314
|
+
def get_unique_words_from_file(file_path):
|
|
315
|
+
seen_words = set()
|
|
316
|
+
with open(file_path, 'r', encoding='utf-8') as file:
|
|
317
|
+
for line in file:
|
|
318
|
+
line_words = line.split()
|
|
319
|
+
for word in line_words:
|
|
320
|
+
if word not in seen_words:
|
|
321
|
+
seen_words.add(word)
|
|
322
|
+
return seen_words
|
|
323
|
+
|
|
324
|
+
failures = []
|
|
325
|
+
executables = []
|
|
326
|
+
try:
|
|
327
|
+
for globname in get_unique_words_from_file(
|
|
328
|
+
os.path.join(self.appman_dir, app, 'remove')):
|
|
329
|
+
results = glob.glob(globname)
|
|
330
|
+
if '/share/applications/AM-ZZZ' in globname:
|
|
331
|
+
for result in results:
|
|
332
|
+
if result.endswith('.desktop'):
|
|
333
|
+
failure = self.launch_desktop_file(result)
|
|
334
|
+
if failure:
|
|
335
|
+
failures.append(' '.join(failure))
|
|
336
|
+
return
|
|
337
|
+
elif '/.local/bin/' in globname:
|
|
338
|
+
for result in results:
|
|
339
|
+
if os.access(result, os.X_OK):
|
|
340
|
+
executables.append(result)
|
|
341
|
+
for executable in executables:
|
|
342
|
+
failure = self.launch_in_terminal(executable)
|
|
343
|
+
if failure:
|
|
344
|
+
failures.append(' '.join(failure))
|
|
345
|
+
return
|
|
346
|
+
except Exception as exc:
|
|
347
|
+
failures += f'cannot find .desktop/executable to run [{exc}]'
|
|
348
|
+
if failures:
|
|
349
|
+
message = ' '.join([f'Cannot launch {app}'] + failures)
|
|
350
|
+
self.win.alert(message=message)
|
|
351
|
+
|
|
352
|
+
self.launch_app(self.pick_app)
|
|
353
|
+
|
|
216
354
|
def do_key(self, key):
|
|
217
355
|
""" TBD """
|
|
218
356
|
if not key:
|
|
@@ -222,21 +360,21 @@ class Vappman:
|
|
|
222
360
|
self.opts.help_mode = False
|
|
223
361
|
return True
|
|
224
362
|
if self.pick_is_installed:
|
|
225
|
-
key = ord('r') #
|
|
363
|
+
key = ord('r') # remove installed app
|
|
226
364
|
else:
|
|
227
365
|
key = ord('i') # install uninstalled app
|
|
228
366
|
|
|
229
367
|
if key in self.spin.keys:
|
|
230
368
|
value = self.spin.do_key(key, self.win)
|
|
231
369
|
return value
|
|
232
|
-
|
|
370
|
+
|
|
233
371
|
if key == 27: # ESCAPE
|
|
234
372
|
self.prev_filter = ''
|
|
235
373
|
self.filter = None
|
|
236
374
|
self.win.pick_pos = 0
|
|
237
375
|
return None
|
|
238
376
|
|
|
239
|
-
if key
|
|
377
|
+
if key in (ord('q'), ord('x')):
|
|
240
378
|
self.win.stop_curses()
|
|
241
379
|
os.system('clear; stty sane')
|
|
242
380
|
sys.exit(0)
|
|
@@ -249,6 +387,10 @@ class Vappman:
|
|
|
249
387
|
self.run_appman(f'appman remove {self.pick_app}')
|
|
250
388
|
return None
|
|
251
389
|
|
|
390
|
+
if key == ord('t') and self.pick_is_installed:
|
|
391
|
+
self.launch_app(self.pick_app)
|
|
392
|
+
return None
|
|
393
|
+
|
|
252
394
|
if key == ord('s'):
|
|
253
395
|
self.run_appman('appman sync')
|
|
254
396
|
if key == ord('c'):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vappman
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8
|
|
4
4
|
Summary: A visual wrapper for appman
|
|
5
5
|
Author-email: Joe Defen <joedef@google.com>
|
|
6
6
|
License: MIT
|
|
@@ -41,7 +41,7 @@ But it does NOT cover:
|
|
|
41
41
|
* (-v) version of appman
|
|
42
42
|
* --force-latest to get the most recent stable release AND
|
|
43
43
|
all other options and unmentioned commands.
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
## Usage
|
|
46
46
|
* Run `vappman` from the command line.
|
|
47
47
|
* It presents some keys available on the top line.
|
|
@@ -67,8 +67,9 @@ But it does NOT cover:
|
|
|
67
67
|
* `ESC` clears the filter and jumps to the top of the listing.
|
|
68
68
|
* Each time the filter is changed, the position jumps to the top of the listing.
|
|
69
69
|
* Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
|
|
70
|
+
* Use `t` to "test" an installed app. This launches a terminal emulator and then the app so you can see issues. This is not for daily use obviously, but for after install or when having unknown issues and you wish to start the investigation.
|
|
70
71
|
|
|
71
|
-
## Example Screenshot
|
|
72
|
+
## Example Screenshot (of v0.7 ... current release will vary slightly)
|
|
72
73
|
.
|
|
73
74
|
|
|
74
75
|
---
|
|
@@ -84,3 +85,5 @@ NOTES: in this example:
|
|
|
84
85
|
* the fixed top line shows some of the available action keys (e.g., `q` quits the app)
|
|
85
86
|
* use `?` to open the help screen describing all keys (including navigation)
|
|
86
87
|
|
|
88
|
+
## Screen Recording (Intro to vappman based on v0.7)
|
|
89
|
+
[](https://youtu.be/NUHYN9_DZtA)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|