skilleter-thingy 0.2.0__py3-none-any.whl → 0.2.1__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.
Potentially problematic release.
This version of skilleter-thingy might be problematic. Click here for more details.
- skilleter_thingy/borger.py +273 -0
- skilleter_thingy/diskspacecheck.py +67 -0
- skilleter_thingy/ggit.py +1 -0
- skilleter_thingy/ggrep.py +1 -0
- skilleter_thingy/git_br.py +7 -0
- skilleter_thingy/git_ca.py +8 -0
- skilleter_thingy/git_cleanup.py +11 -0
- skilleter_thingy/git_co.py +8 -3
- skilleter_thingy/git_common.py +12 -4
- skilleter_thingy/git_hold.py +9 -0
- skilleter_thingy/git_mr.py +11 -0
- skilleter_thingy/git_parent.py +23 -18
- skilleter_thingy/git_retag.py +10 -0
- skilleter_thingy/git_review.py +1 -0
- skilleter_thingy/git_update.py +1 -0
- skilleter_thingy/git_wt.py +2 -0
- skilleter_thingy/gitprompt.py +1 -0
- skilleter_thingy/localphotosync.py +201 -0
- skilleter_thingy/moviemover.py +133 -0
- skilleter_thingy/photodupe.py +135 -0
- skilleter_thingy/phototidier.py +248 -0
- skilleter_thingy/splitpics.py +99 -0
- skilleter_thingy/sysmon.py +435 -0
- skilleter_thingy/thingy/git.py +18 -5
- skilleter_thingy/thingy/git2.py +20 -7
- skilleter_thingy/window_rename.py +92 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/METADATA +46 -1
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/RECORD +32 -23
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/entry_points.txt +9 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/WHEEL +0 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
################################################################################
|
|
4
|
+
"""Very simple system monitoring dashboard"""
|
|
5
|
+
################################################################################
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import datetime
|
|
9
|
+
import time
|
|
10
|
+
import curses
|
|
11
|
+
|
|
12
|
+
import psutil
|
|
13
|
+
|
|
14
|
+
################################################################################
|
|
15
|
+
|
|
16
|
+
NUM_BOXES_V = 5
|
|
17
|
+
NUM_BOXES_H = 2
|
|
18
|
+
|
|
19
|
+
UPDATE_PERIOD = 1
|
|
20
|
+
|
|
21
|
+
################################################################################
|
|
22
|
+
|
|
23
|
+
def show_system_load(scr, first, w, h, x, y):
|
|
24
|
+
"""Load averaged"""
|
|
25
|
+
|
|
26
|
+
load = psutil.getloadavg()
|
|
27
|
+
|
|
28
|
+
x += 2
|
|
29
|
+
|
|
30
|
+
if first:
|
|
31
|
+
scr.addstr(y+1, x, '1 minute:')
|
|
32
|
+
scr.addstr(y+2, x, '5 minute:')
|
|
33
|
+
scr.addstr(y+3, x, '15 minute:')
|
|
34
|
+
else:
|
|
35
|
+
scr.addstr(y+1, x+10, f'{load[0]:6.2f}')
|
|
36
|
+
scr.addstr(y+2, x+10, f'{load[1]:6.2f}')
|
|
37
|
+
scr.addstr(y+3, x+10, f'{load[2]:6.2f}')
|
|
38
|
+
|
|
39
|
+
################################################################################
|
|
40
|
+
|
|
41
|
+
def show_cpu_times(scr, first, w, h, x, y):
|
|
42
|
+
"""CPU times"""
|
|
43
|
+
|
|
44
|
+
info = psutil.cpu_times()
|
|
45
|
+
|
|
46
|
+
x += 2
|
|
47
|
+
|
|
48
|
+
if first:
|
|
49
|
+
scr.addstr(y+1, x, 'Idle:')
|
|
50
|
+
scr.addstr(y+2, x, 'System:')
|
|
51
|
+
scr.addstr(y+3, x, 'User:')
|
|
52
|
+
scr.addstr(y+4, x, 'Nice:')
|
|
53
|
+
|
|
54
|
+
x += w//3
|
|
55
|
+
|
|
56
|
+
scr.addstr(y+1, x, 'I/O Wait:')
|
|
57
|
+
scr.addstr(y+2, x, 'IRQ:')
|
|
58
|
+
scr.addstr(y+3, x, 'Soft IRQ:')
|
|
59
|
+
|
|
60
|
+
x += w//3
|
|
61
|
+
|
|
62
|
+
scr.addstr(y+1, x, 'Guest:')
|
|
63
|
+
scr.addstr(y+2, x, 'Guest Nice:')
|
|
64
|
+
else:
|
|
65
|
+
total = (info.user + info.system + info.idle + info.nice + info.iowait + info.irq + info.softirq + info.guest + info.guest_nice)/100
|
|
66
|
+
|
|
67
|
+
user = info.user / total
|
|
68
|
+
system = info.system / total
|
|
69
|
+
idle = info.idle / total
|
|
70
|
+
nice = info.nice / total
|
|
71
|
+
iowait = info.iowait / total
|
|
72
|
+
irq = info.irq / total
|
|
73
|
+
softirq = info.softirq / total
|
|
74
|
+
guest = info.guest / total
|
|
75
|
+
guest_nice = info.guest_nice / total
|
|
76
|
+
|
|
77
|
+
scr.addstr(y+1, x+9, f'{idle:6.2f}')
|
|
78
|
+
scr.addstr(y+2, x+9, f'{system:6.2f}')
|
|
79
|
+
scr.addstr(y+3, x+9, f'{user:6.2f}')
|
|
80
|
+
scr.addstr(y+4, x+9, f'{nice:6.2f}')
|
|
81
|
+
|
|
82
|
+
x += w//3
|
|
83
|
+
|
|
84
|
+
scr.addstr(y+1, x+10, f'{iowait:6.2f}')
|
|
85
|
+
scr.addstr(y+2, x+10, f'{irq:6.2f}')
|
|
86
|
+
scr.addstr(y+3, x+10, f'{softirq:6.2f}')
|
|
87
|
+
|
|
88
|
+
x += w//3
|
|
89
|
+
|
|
90
|
+
scr.addstr(y+1, x+12, f'{guest:6.2f}')
|
|
91
|
+
scr.addstr(y+2, x+12, f'{guest_nice:6.2f}')
|
|
92
|
+
|
|
93
|
+
################################################################################
|
|
94
|
+
|
|
95
|
+
def show_disk_access(scr, first, w, h, x, y):
|
|
96
|
+
"""Disk I/O statistics"""
|
|
97
|
+
|
|
98
|
+
info = psutil.disk_io_counters()
|
|
99
|
+
|
|
100
|
+
x += 2
|
|
101
|
+
|
|
102
|
+
if first:
|
|
103
|
+
scr.addstr(y+1, x, 'Read count:')
|
|
104
|
+
scr.addstr(y+2, x, 'Write count:')
|
|
105
|
+
|
|
106
|
+
scr.addstr(y+4, x, 'Read bytes:')
|
|
107
|
+
scr.addstr(y+5, x, 'Write bytes:')
|
|
108
|
+
|
|
109
|
+
x += w//3
|
|
110
|
+
|
|
111
|
+
scr.addstr(y+1, x, 'Read time:')
|
|
112
|
+
scr.addstr(y+2, x, 'Write time:')
|
|
113
|
+
|
|
114
|
+
scr.addstr(y+4, x, 'I/O time:')
|
|
115
|
+
|
|
116
|
+
x += w//3
|
|
117
|
+
|
|
118
|
+
scr.addstr(y+1, x, 'Read merged:')
|
|
119
|
+
scr.addstr(y+2, x, 'Write merged:')
|
|
120
|
+
else:
|
|
121
|
+
scr.addstr(y+1, x+14, f'{info.read_count:12}')
|
|
122
|
+
scr.addstr(y+2, x+14, f'{info.write_count:12}')
|
|
123
|
+
|
|
124
|
+
scr.addstr(y+4, x+14, f'{info.read_bytes:12}')
|
|
125
|
+
scr.addstr(y+5, x+14, f'{info.write_bytes:12}')
|
|
126
|
+
|
|
127
|
+
x += w//3
|
|
128
|
+
|
|
129
|
+
scr.addstr(y+1, x+14, f'{info.read_time:12}')
|
|
130
|
+
scr.addstr(y+2, x+14, f'{info.write_time:12}')
|
|
131
|
+
|
|
132
|
+
scr.addstr(y+4, x+14, f'{info.busy_time:12}')
|
|
133
|
+
|
|
134
|
+
x += w//3
|
|
135
|
+
|
|
136
|
+
scr.addstr(y+1, x+14, f'{info.read_merged_count:12}')
|
|
137
|
+
scr.addstr(y+2, x+14, f'{info.write_merged_count:12}')
|
|
138
|
+
|
|
139
|
+
################################################################################
|
|
140
|
+
|
|
141
|
+
def show_processes(scr, first, w, h, x, y):
|
|
142
|
+
"""TBD: Process information"""
|
|
143
|
+
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
################################################################################
|
|
147
|
+
|
|
148
|
+
def show_memory(scr, first, w, h, x, y):
|
|
149
|
+
"""Memory usage"""
|
|
150
|
+
|
|
151
|
+
x += 2
|
|
152
|
+
|
|
153
|
+
if first:
|
|
154
|
+
scr.addstr(y+1, x, 'Total:')
|
|
155
|
+
scr.addstr(y+2, x, 'Used:')
|
|
156
|
+
scr.addstr(y+3, x, 'Buffers:')
|
|
157
|
+
scr.addstr(y+4, x, 'Free:')
|
|
158
|
+
|
|
159
|
+
x += w//3
|
|
160
|
+
|
|
161
|
+
scr.addstr(y+1, x, 'Active:')
|
|
162
|
+
scr.addstr(y+2, x, 'Inactive:')
|
|
163
|
+
|
|
164
|
+
scr.addstr(y+4, x, 'Usage:')
|
|
165
|
+
|
|
166
|
+
x += w//3
|
|
167
|
+
|
|
168
|
+
scr.addstr(y+1, x, 'Shared:')
|
|
169
|
+
scr.addstr(y+2, x, 'Slab:')
|
|
170
|
+
else:
|
|
171
|
+
meminfo = psutil.virtual_memory()
|
|
172
|
+
x += 11
|
|
173
|
+
|
|
174
|
+
scr.addstr(y+1, x, f'{meminfo.total:12}')
|
|
175
|
+
scr.addstr(y+2, x, f'{meminfo.used:12}')
|
|
176
|
+
scr.addstr(y+3, x, f'{meminfo.buffers:12}')
|
|
177
|
+
scr.addstr(y+4, x, f'{meminfo.free:12}')
|
|
178
|
+
|
|
179
|
+
x += w//3
|
|
180
|
+
|
|
181
|
+
scr.addstr(y+1, x, f'{meminfo.active:12}')
|
|
182
|
+
scr.addstr(y+2, x, f'{meminfo.inactive:12}')
|
|
183
|
+
|
|
184
|
+
scr.addstr(y+4, x, f'{meminfo.percent:6.1f}%')
|
|
185
|
+
|
|
186
|
+
x += w//3
|
|
187
|
+
|
|
188
|
+
scr.addstr(y+1, x, f'{meminfo.shared:12}')
|
|
189
|
+
scr.addstr(y+2, x, f'{meminfo.slab:12}')
|
|
190
|
+
|
|
191
|
+
################################################################################
|
|
192
|
+
|
|
193
|
+
def show_voltages(scr, first, w, h, x, y):
|
|
194
|
+
"""TBD: Voltages"""
|
|
195
|
+
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
################################################################################
|
|
199
|
+
|
|
200
|
+
def show_cpu_load(scr, first, w, h, x, y):
|
|
201
|
+
"""CPU load and frequencies"""
|
|
202
|
+
|
|
203
|
+
info = psutil.cpu_percent(percpu=True)
|
|
204
|
+
freq = psutil.cpu_freq(percpu=True)
|
|
205
|
+
|
|
206
|
+
stats = psutil.cpu_stats()
|
|
207
|
+
|
|
208
|
+
xo = yo = 0
|
|
209
|
+
for n, cpu in enumerate(info):
|
|
210
|
+
if first:
|
|
211
|
+
scr.addstr(y+yo+1, x+xo+5, 'CPU # : % at MHz')
|
|
212
|
+
else:
|
|
213
|
+
scr.addstr(y+yo+1, x+xo+10, f'{n:<2}')
|
|
214
|
+
scr.addstr(y+yo+1, x+xo+14, f'{cpu:5.1f}%')
|
|
215
|
+
scr.addstr(y+yo+1, x+xo+23, f'{freq[n].current:8.2f}')
|
|
216
|
+
|
|
217
|
+
yo += 1
|
|
218
|
+
|
|
219
|
+
if yo > h-2:
|
|
220
|
+
xo += w//3
|
|
221
|
+
yo = 0
|
|
222
|
+
|
|
223
|
+
x += w//2
|
|
224
|
+
|
|
225
|
+
if first:
|
|
226
|
+
scr.addstr(y+1, x, 'Context switches:')
|
|
227
|
+
scr.addstr(y+2, x, 'Interrupts:')
|
|
228
|
+
scr.addstr(y+3, x, 'Soft interrupts:')
|
|
229
|
+
else:
|
|
230
|
+
x += 18
|
|
231
|
+
scr.addstr(y+1, x, f'{stats.ctx_switches:12}')
|
|
232
|
+
scr.addstr(y+2, x, f'{stats.interrupts:12}')
|
|
233
|
+
scr.addstr(y+3, x, f'{stats.soft_interrupts:12}')
|
|
234
|
+
|
|
235
|
+
################################################################################
|
|
236
|
+
|
|
237
|
+
def show_swappery(scr, first, w, h, x, y):
|
|
238
|
+
"""Swap info"""
|
|
239
|
+
|
|
240
|
+
x += 2
|
|
241
|
+
|
|
242
|
+
if first:
|
|
243
|
+
scr.addstr(y+1, x, 'Swap total:')
|
|
244
|
+
scr.addstr(y+2, x, 'Swap used:')
|
|
245
|
+
scr.addstr(y+3, x, 'Swap free:')
|
|
246
|
+
|
|
247
|
+
x += w//3
|
|
248
|
+
|
|
249
|
+
scr.addstr(y+1, x, 'Swap used:')
|
|
250
|
+
|
|
251
|
+
x += w//3
|
|
252
|
+
|
|
253
|
+
scr.addstr(y+1, x, 'Swapped in:')
|
|
254
|
+
scr.addstr(y+2, x, 'Swapped out:')
|
|
255
|
+
else:
|
|
256
|
+
info = psutil.swap_memory()
|
|
257
|
+
|
|
258
|
+
x += 14
|
|
259
|
+
|
|
260
|
+
scr.addstr(y+1, x, f'{info.total:12}')
|
|
261
|
+
scr.addstr(y+2, x, f'{info.used:12}')
|
|
262
|
+
scr.addstr(y+3, x, f'{info.free:12}')
|
|
263
|
+
|
|
264
|
+
x += w//3
|
|
265
|
+
|
|
266
|
+
scr.addstr(y+1, x, f'{info.percent:6.2f}%')
|
|
267
|
+
|
|
268
|
+
x += w//3
|
|
269
|
+
|
|
270
|
+
scr.addstr(y+1, x, f'{info.sin:12}')
|
|
271
|
+
scr.addstr(y+2, x, f'{info.sout:12}')
|
|
272
|
+
|
|
273
|
+
################################################################################
|
|
274
|
+
|
|
275
|
+
def show_network(scr, first, w, h, x, y):
|
|
276
|
+
"""Network statistics"""
|
|
277
|
+
|
|
278
|
+
x += 2
|
|
279
|
+
|
|
280
|
+
if first:
|
|
281
|
+
scr.addstr(y+1, x, 'Bytes sent:')
|
|
282
|
+
scr.addstr(y+2, x, 'Bytes received:')
|
|
283
|
+
|
|
284
|
+
scr.addstr(y+4, x, 'Packets sent:')
|
|
285
|
+
scr.addstr(y+5, x, 'Packets received:')
|
|
286
|
+
|
|
287
|
+
x += w//2
|
|
288
|
+
|
|
289
|
+
scr.addstr(y+1, x, 'Send errors:')
|
|
290
|
+
scr.addstr(y+2, x, 'Receive errors:')
|
|
291
|
+
|
|
292
|
+
scr.addstr(y+4, x, 'Outgoing dropped:')
|
|
293
|
+
scr.addstr(y+5, x, 'Incoming dropped:')
|
|
294
|
+
else:
|
|
295
|
+
info = psutil.net_io_counters()
|
|
296
|
+
x += 19
|
|
297
|
+
|
|
298
|
+
scr.addstr(y+1, x, f'{info.bytes_sent:12}')
|
|
299
|
+
scr.addstr(y+2, x, f'{info.bytes_recv:12}')
|
|
300
|
+
|
|
301
|
+
scr.addstr(y+4, x, f'{info.packets_sent:12}')
|
|
302
|
+
scr.addstr(y+5, x, f'{info.packets_recv:12}')
|
|
303
|
+
|
|
304
|
+
x += w//2
|
|
305
|
+
|
|
306
|
+
scr.addstr(y+1, x, f'{info.errout:12}')
|
|
307
|
+
scr.addstr(y+2, x, f'{info.errin:12}')
|
|
308
|
+
|
|
309
|
+
scr.addstr(y+4, x, f'{info.dropout:12}')
|
|
310
|
+
scr.addstr(y+5, x, f'{info.dropin:12}')
|
|
311
|
+
|
|
312
|
+
################################################################################
|
|
313
|
+
|
|
314
|
+
def show_temperatures(scr, first, w, h, x, y):
|
|
315
|
+
"""TBD: Temperatures"""
|
|
316
|
+
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
################################################################################
|
|
320
|
+
|
|
321
|
+
# Panel title and the functions used to update them
|
|
322
|
+
|
|
323
|
+
BOXES = {
|
|
324
|
+
'System Load': show_system_load,
|
|
325
|
+
'Disk Access': show_disk_access,
|
|
326
|
+
'Processes': show_processes,
|
|
327
|
+
'Memory': show_memory,
|
|
328
|
+
'Voltages': show_voltages,
|
|
329
|
+
|
|
330
|
+
'CPU Load': show_cpu_load,
|
|
331
|
+
'Swap and Paging': show_swappery,
|
|
332
|
+
'Temperatures': show_temperatures,
|
|
333
|
+
'Network': show_network,
|
|
334
|
+
'Total CPU Times': show_cpu_times,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
################################################################################
|
|
338
|
+
|
|
339
|
+
def main(stdscr):
|
|
340
|
+
"""Main function"""
|
|
341
|
+
|
|
342
|
+
# Configure curses
|
|
343
|
+
|
|
344
|
+
curses.curs_set(0)
|
|
345
|
+
curses.start_color()
|
|
346
|
+
curses.noecho()
|
|
347
|
+
curses.cbreak()
|
|
348
|
+
curses.use_default_colors()
|
|
349
|
+
|
|
350
|
+
curses.init_pair(1, curses.COLOR_GREEN, 15)
|
|
351
|
+
curses.init_pair(2, curses.COLOR_BLUE, 15)
|
|
352
|
+
|
|
353
|
+
# Set up the display
|
|
354
|
+
|
|
355
|
+
stdscr.keypad(1)
|
|
356
|
+
stdscr.nodelay(True)
|
|
357
|
+
stdscr.bkgdset(' ', curses.color_pair(0))
|
|
358
|
+
|
|
359
|
+
# Outer loop iterates whenever the console window changes size
|
|
360
|
+
|
|
361
|
+
terminate = False
|
|
362
|
+
|
|
363
|
+
while not terminate:
|
|
364
|
+
stdscr.clear()
|
|
365
|
+
|
|
366
|
+
height, width = stdscr.getmaxyx()
|
|
367
|
+
|
|
368
|
+
box_h = height // NUM_BOXES_V
|
|
369
|
+
box_w = width // NUM_BOXES_H
|
|
370
|
+
|
|
371
|
+
# Draw the titles and text on the first iteration
|
|
372
|
+
# Just draw the statistics on the subsequent ones
|
|
373
|
+
|
|
374
|
+
first_time = True
|
|
375
|
+
window_resize = False
|
|
376
|
+
|
|
377
|
+
# Inner loop just updates display until
|
|
378
|
+
|
|
379
|
+
while not window_resize and not terminate:
|
|
380
|
+
now = datetime.datetime.now()
|
|
381
|
+
|
|
382
|
+
stdscr.addstr(0, 1, now.strftime('%02H:%02M:%02S'), curses.COLOR_BLACK)
|
|
383
|
+
stdscr.addstr(0, width-11, now.strftime('%02Y-%02m-%02d'), curses.COLOR_BLACK)
|
|
384
|
+
|
|
385
|
+
for i, box in enumerate(BOXES):
|
|
386
|
+
x, y = divmod(i, NUM_BOXES_V)
|
|
387
|
+
|
|
388
|
+
x *= box_w
|
|
389
|
+
y *= box_h
|
|
390
|
+
|
|
391
|
+
title_x = x+(box_w - len(box))//2
|
|
392
|
+
stdscr.addstr(y, title_x, box, curses.A_BOLD)
|
|
393
|
+
|
|
394
|
+
stdscr.attron(curses.color_pair(1 if first_time else 2))
|
|
395
|
+
|
|
396
|
+
BOXES[box](stdscr, first_time, box_w, box_h, x, y+1)
|
|
397
|
+
|
|
398
|
+
# Update the display, clear the first-time draw-static-content flag
|
|
399
|
+
|
|
400
|
+
stdscr.refresh()
|
|
401
|
+
first_time = False
|
|
402
|
+
|
|
403
|
+
# Wait for the next update
|
|
404
|
+
|
|
405
|
+
elapsed = (datetime.datetime.now() - now).total_seconds()
|
|
406
|
+
|
|
407
|
+
if elapsed < UPDATE_PERIOD:
|
|
408
|
+
time.sleep(UPDATE_PERIOD - elapsed)
|
|
409
|
+
|
|
410
|
+
# Check for keypress or window resize
|
|
411
|
+
|
|
412
|
+
keyboard = stdscr.getch()
|
|
413
|
+
|
|
414
|
+
if keyboard in (ord('Q'), ord('q')):
|
|
415
|
+
terminate = True
|
|
416
|
+
|
|
417
|
+
elif keyboard == curses.KEY_RESIZE:
|
|
418
|
+
window_resize = True
|
|
419
|
+
|
|
420
|
+
################################################################################
|
|
421
|
+
|
|
422
|
+
def sysmon():
|
|
423
|
+
"""Entry point"""
|
|
424
|
+
|
|
425
|
+
try:
|
|
426
|
+
curses.wrapper(main)
|
|
427
|
+
except KeyboardInterrupt:
|
|
428
|
+
sys.exit(1)
|
|
429
|
+
except BrokenPipeError:
|
|
430
|
+
sys.exit(2)
|
|
431
|
+
|
|
432
|
+
################################################################################
|
|
433
|
+
|
|
434
|
+
if __name__ == "__main__":
|
|
435
|
+
sysmon()
|
skilleter_thingy/thingy/git.py
CHANGED
|
@@ -34,7 +34,11 @@ import thingy.gitlab as gitlab
|
|
|
34
34
|
################################################################################
|
|
35
35
|
# Configuration files to access
|
|
36
36
|
|
|
37
|
-
(LOCAL, GLOBAL, SYSTEM) = list(range(
|
|
37
|
+
(WORKTREE, LOCAL, GLOBAL, SYSTEM) = list(range(4))
|
|
38
|
+
|
|
39
|
+
# Default default branches (can be overridden in .gitconfig via skilleter-thingy.defaultBranches
|
|
40
|
+
|
|
41
|
+
DEFAULT_DEFAULT_BRANCHES = 'develop,main,master'
|
|
38
42
|
|
|
39
43
|
################################################################################
|
|
40
44
|
|
|
@@ -814,7 +818,7 @@ def reset(sha1):
|
|
|
814
818
|
|
|
815
819
|
################################################################################
|
|
816
820
|
|
|
817
|
-
def config_get(section, key, source=
|
|
821
|
+
def config_get(section, key, source=None, defaultvalue=None):
|
|
818
822
|
""" Return the specified configuration entry
|
|
819
823
|
Returns a default value if no matching configuration entry exists """
|
|
820
824
|
|
|
@@ -824,6 +828,10 @@ def config_get(section, key, source=LOCAL, defaultvalue=None):
|
|
|
824
828
|
cmd.append('--global')
|
|
825
829
|
elif source == SYSTEM:
|
|
826
830
|
cmd.append('--system')
|
|
831
|
+
elif source == LOCAL:
|
|
832
|
+
cmd.append('--local')
|
|
833
|
+
elif source == WORKTREE:
|
|
834
|
+
cmd.append('--worktree')
|
|
827
835
|
|
|
828
836
|
cmd += ['--get', f'{section}.{key}']
|
|
829
837
|
|
|
@@ -834,7 +842,7 @@ def config_get(section, key, source=LOCAL, defaultvalue=None):
|
|
|
834
842
|
|
|
835
843
|
################################################################################
|
|
836
844
|
|
|
837
|
-
def config_set(section, key, value, source=
|
|
845
|
+
def config_set(section, key, value, source=None):
|
|
838
846
|
""" Set a configuration entry """
|
|
839
847
|
|
|
840
848
|
cmd = ['config']
|
|
@@ -843,6 +851,10 @@ def config_set(section, key, value, source=LOCAL):
|
|
|
843
851
|
cmd.append('--global')
|
|
844
852
|
elif source == SYSTEM:
|
|
845
853
|
cmd.append('--system')
|
|
854
|
+
elif source == LOCAL:
|
|
855
|
+
cmd.append('--local')
|
|
856
|
+
elif source == WORKTREE:
|
|
857
|
+
cmd.append('--worktree')
|
|
846
858
|
|
|
847
859
|
cmd += ['--replace-all', f'{section}.{key}', value]
|
|
848
860
|
|
|
@@ -1172,12 +1184,13 @@ def default_branch():
|
|
|
1172
1184
|
return None
|
|
1173
1185
|
|
|
1174
1186
|
git_branches = branches()
|
|
1187
|
+
default_branches = config_get('skilleter-thingy', 'defaultBranches', defaultvalue=DEFAULT_DEFAULT_BRANCHES).split(',')
|
|
1175
1188
|
|
|
1176
|
-
for branch in
|
|
1189
|
+
for branch in default_branches:
|
|
1177
1190
|
if branch in git_branches:
|
|
1178
1191
|
return branch
|
|
1179
1192
|
|
|
1180
|
-
|
|
1193
|
+
raise GitError('Unable to determine default branch in the repo')
|
|
1181
1194
|
|
|
1182
1195
|
################################################################################
|
|
1183
1196
|
|
skilleter_thingy/thingy/git2.py
CHANGED
|
@@ -36,13 +36,17 @@ import thingy.gitlab as gitlab
|
|
|
36
36
|
################################################################################
|
|
37
37
|
# Configuration files to access
|
|
38
38
|
|
|
39
|
-
(LOCAL, GLOBAL, SYSTEM) = list(range(
|
|
39
|
+
(WORKTREE, LOCAL, GLOBAL, SYSTEM) = list(range(4))
|
|
40
40
|
|
|
41
41
|
# Options always passed to Git (disable autocorrect to stop it running the wrong command
|
|
42
42
|
# if an invalid command name has been specified).
|
|
43
43
|
|
|
44
44
|
STANDARD_GIT_OPTIONS = ['-c', 'help.autoCorrect=never']
|
|
45
45
|
|
|
46
|
+
# Default default branches (can be overridden in .gitconfig via skilleter-thingy.defaultBranches
|
|
47
|
+
|
|
48
|
+
DEFAULT_DEFAULT_BRANCHES = 'develop,main,master'
|
|
49
|
+
|
|
46
50
|
################################################################################
|
|
47
51
|
|
|
48
52
|
class GitError(run.RunError):
|
|
@@ -74,7 +78,7 @@ def git(cmd, stdout=None, stderr=None, path=None):
|
|
|
74
78
|
try:
|
|
75
79
|
return run.run(git_cmd, stdout=stdout, stderr=stderr)
|
|
76
80
|
except run.RunError as exc:
|
|
77
|
-
raise GitError(exc.msg, exc.status)
|
|
81
|
+
raise GitError(exc.msg, exc.status) from exc
|
|
78
82
|
|
|
79
83
|
################################################################################
|
|
80
84
|
|
|
@@ -103,7 +107,7 @@ def git_run_status(cmd, stdout=None, stderr=None, path=None, redirect=True):
|
|
|
103
107
|
errors='ignore',
|
|
104
108
|
universal_newlines=True)
|
|
105
109
|
else:
|
|
106
|
-
result = subprocess.run(git_cmd)
|
|
110
|
+
result = subprocess.run(git_cmd, check=False)
|
|
107
111
|
|
|
108
112
|
return (result.stdout or result.stderr), result.returncode
|
|
109
113
|
|
|
@@ -775,7 +779,7 @@ def reset(sha1, path=None):
|
|
|
775
779
|
|
|
776
780
|
################################################################################
|
|
777
781
|
|
|
778
|
-
def config_get(section, key, source=
|
|
782
|
+
def config_get(section, key, source=None, defaultvalue=None, path=None):
|
|
779
783
|
""" Return the specified configuration entry
|
|
780
784
|
Returns a default value if no matching configuration entry exists """
|
|
781
785
|
|
|
@@ -785,6 +789,10 @@ def config_get(section, key, source=LOCAL, defaultvalue=None, path=None):
|
|
|
785
789
|
cmd.append('--global')
|
|
786
790
|
elif source == SYSTEM:
|
|
787
791
|
cmd.append('--system')
|
|
792
|
+
elif source == LOCAL:
|
|
793
|
+
cmd.append('--local')
|
|
794
|
+
elif source == WORKTREE:
|
|
795
|
+
cmd.append('--worktree')
|
|
788
796
|
|
|
789
797
|
cmd += ['--get', f'{section}.{key}']
|
|
790
798
|
|
|
@@ -795,7 +803,7 @@ def config_get(section, key, source=LOCAL, defaultvalue=None, path=None):
|
|
|
795
803
|
|
|
796
804
|
################################################################################
|
|
797
805
|
|
|
798
|
-
def config_set(section, key, value, source=
|
|
806
|
+
def config_set(section, key, value, source=None, path=None):
|
|
799
807
|
""" Set a configuration entry """
|
|
800
808
|
|
|
801
809
|
cmd = ['config']
|
|
@@ -804,6 +812,10 @@ def config_set(section, key, value, source=LOCAL, path=None):
|
|
|
804
812
|
cmd.append('--global')
|
|
805
813
|
elif source == SYSTEM:
|
|
806
814
|
cmd.append('--system')
|
|
815
|
+
elif source == LOCAL:
|
|
816
|
+
cmd.append('--local')
|
|
817
|
+
elif source == WORKTREE:
|
|
818
|
+
cmd.append('--worktree')
|
|
807
819
|
|
|
808
820
|
cmd += ['--replace-all', f'{section}.{key}', value]
|
|
809
821
|
|
|
@@ -1130,12 +1142,13 @@ def default_branch(path=None):
|
|
|
1130
1142
|
return None
|
|
1131
1143
|
|
|
1132
1144
|
git_branches = branches(path=path)
|
|
1145
|
+
default_branches = config_get('skilleter-thingy', 'defaultBranches', defaultvalue=DEFAULT_DEFAULT_BRANCHES).split(',')
|
|
1133
1146
|
|
|
1134
|
-
for branch in
|
|
1147
|
+
for branch in default_branches:
|
|
1135
1148
|
if branch in git_branches:
|
|
1136
1149
|
return branch
|
|
1137
1150
|
|
|
1138
|
-
|
|
1151
|
+
raise GitError('Unable to determine default branch in the repo')
|
|
1139
1152
|
|
|
1140
1153
|
################################################################################
|
|
1141
1154
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
################################################################################
|
|
4
|
+
"""
|
|
5
|
+
Monitor window titles and rename them to fit an alphabetical grouping
|
|
6
|
+
in 'Appname - Document' format.
|
|
7
|
+
"""
|
|
8
|
+
################################################################################
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import re
|
|
12
|
+
import subprocess
|
|
13
|
+
|
|
14
|
+
################################################################################
|
|
15
|
+
# Hard coded table of regexes for window titles to rename
|
|
16
|
+
# TODO: This should be a configuration file
|
|
17
|
+
|
|
18
|
+
RENAMES = [
|
|
19
|
+
[r'^\[(.*)\]$', r'\1'],
|
|
20
|
+
[r'(.*) - Mozilla Firefox', r'Firefox - \1'],
|
|
21
|
+
[r'(.*) - Mozilla Thunderbird', r'Thunderbird - \1'],
|
|
22
|
+
[r'\[(.*) - KeePass\]', r'Keepass - \1'],
|
|
23
|
+
[r'(.*) - LibreOffice Calc', r'LibreOffice Calc - \1'],
|
|
24
|
+
[r'(.*) - Zim$', r'Zim - \1'],
|
|
25
|
+
[r'(.*) - Chromium$', r'Chromium - \1'],
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
################################################################################
|
|
29
|
+
|
|
30
|
+
def main():
|
|
31
|
+
""" Search for windows and rename them appropriately """
|
|
32
|
+
|
|
33
|
+
# Build the re. to match renameable windows
|
|
34
|
+
|
|
35
|
+
regex = '|'.join([rename[0] for rename in RENAMES])
|
|
36
|
+
|
|
37
|
+
# Build the command to wait for a matching visible window to appear
|
|
38
|
+
|
|
39
|
+
cmd = ['xdotool', 'search', '--sync', '--name', '--onlyvisible', regex]
|
|
40
|
+
|
|
41
|
+
while True:
|
|
42
|
+
# Wait for one or more matching windows
|
|
43
|
+
|
|
44
|
+
result = subprocess.run(cmd, stdout=subprocess.PIPE, universal_newlines=True)
|
|
45
|
+
|
|
46
|
+
if result.returncode:
|
|
47
|
+
sys.stderr.write('ERROR %d returned from xdotool search, ignoring it.\n' % result.returncode)
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
# Parse the list of window IDs
|
|
51
|
+
|
|
52
|
+
for window_id in result.stdout.split('\n'):
|
|
53
|
+
if window_id:
|
|
54
|
+
# Get the window name
|
|
55
|
+
|
|
56
|
+
names = subprocess.run(['xdotool', 'getwindowname', window_id], stdout=subprocess.PIPE, universal_newlines=True)
|
|
57
|
+
|
|
58
|
+
if result.returncode:
|
|
59
|
+
sys.stderr.write('ERROR %d returned from xdotool getwindowname, ignoring it.\n' % result.returncode)
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
name = names.stdout.split('\n')[0].strip()
|
|
63
|
+
|
|
64
|
+
if name:
|
|
65
|
+
# If it matches an entry in the list then rename it
|
|
66
|
+
|
|
67
|
+
for entry in RENAMES:
|
|
68
|
+
new_name = re.sub(entry[0], entry[1], name)
|
|
69
|
+
|
|
70
|
+
if new_name != name:
|
|
71
|
+
print('%s -> %s' % (name, new_name))
|
|
72
|
+
subprocess.run(['wmctrl', '-i', '-r', window_id, '-N', new_name])
|
|
73
|
+
if result.returncode:
|
|
74
|
+
sys.stderr.write('ERROR %d returned from wmctrl set_window, ignoring it.\n' % result.returncode)
|
|
75
|
+
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
################################################################################
|
|
79
|
+
|
|
80
|
+
def window_rename():
|
|
81
|
+
"""Entry point"""
|
|
82
|
+
try:
|
|
83
|
+
main()
|
|
84
|
+
except KeyboardInterrupt:
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
except BrokenPipeError:
|
|
87
|
+
sys.exit(2)
|
|
88
|
+
|
|
89
|
+
################################################################################
|
|
90
|
+
|
|
91
|
+
if __name__ == '__main__':
|
|
92
|
+
window_rename()
|