skilleter-thingy 0.0.22__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.

Files changed (67) hide show
  1. skilleter_thingy/__init__.py +0 -0
  2. skilleter_thingy/addpath.py +107 -0
  3. skilleter_thingy/aws.py +548 -0
  4. skilleter_thingy/borger.py +269 -0
  5. skilleter_thingy/colour.py +213 -0
  6. skilleter_thingy/console_colours.py +63 -0
  7. skilleter_thingy/dc_curses.py +278 -0
  8. skilleter_thingy/dc_defaults.py +221 -0
  9. skilleter_thingy/dc_util.py +50 -0
  10. skilleter_thingy/dircolors.py +308 -0
  11. skilleter_thingy/diskspacecheck.py +67 -0
  12. skilleter_thingy/docker.py +95 -0
  13. skilleter_thingy/docker_purge.py +113 -0
  14. skilleter_thingy/ffind.py +536 -0
  15. skilleter_thingy/files.py +142 -0
  16. skilleter_thingy/ggit.py +90 -0
  17. skilleter_thingy/ggrep.py +154 -0
  18. skilleter_thingy/git.py +1368 -0
  19. skilleter_thingy/git2.py +1307 -0
  20. skilleter_thingy/git_br.py +180 -0
  21. skilleter_thingy/git_ca.py +142 -0
  22. skilleter_thingy/git_cleanup.py +287 -0
  23. skilleter_thingy/git_co.py +220 -0
  24. skilleter_thingy/git_common.py +61 -0
  25. skilleter_thingy/git_hold.py +154 -0
  26. skilleter_thingy/git_mr.py +92 -0
  27. skilleter_thingy/git_parent.py +77 -0
  28. skilleter_thingy/git_review.py +1416 -0
  29. skilleter_thingy/git_update.py +385 -0
  30. skilleter_thingy/git_wt.py +96 -0
  31. skilleter_thingy/gitcmp_helper.py +322 -0
  32. skilleter_thingy/gitlab.py +193 -0
  33. skilleter_thingy/gitprompt.py +274 -0
  34. skilleter_thingy/gl.py +174 -0
  35. skilleter_thingy/gphotosync.py +610 -0
  36. skilleter_thingy/linecount.py +155 -0
  37. skilleter_thingy/logger.py +112 -0
  38. skilleter_thingy/moviemover.py +133 -0
  39. skilleter_thingy/path.py +156 -0
  40. skilleter_thingy/photodupe.py +110 -0
  41. skilleter_thingy/phototidier.py +248 -0
  42. skilleter_thingy/popup.py +87 -0
  43. skilleter_thingy/process.py +112 -0
  44. skilleter_thingy/py_audit.py +131 -0
  45. skilleter_thingy/readable.py +270 -0
  46. skilleter_thingy/remdir.py +126 -0
  47. skilleter_thingy/rmdupe.py +550 -0
  48. skilleter_thingy/rpylint.py +91 -0
  49. skilleter_thingy/run.py +334 -0
  50. skilleter_thingy/s3_sync.py +383 -0
  51. skilleter_thingy/splitpics.py +99 -0
  52. skilleter_thingy/strreplace.py +82 -0
  53. skilleter_thingy/sysmon.py +435 -0
  54. skilleter_thingy/tfm.py +920 -0
  55. skilleter_thingy/tfm_pane.py +595 -0
  56. skilleter_thingy/tfparse.py +101 -0
  57. skilleter_thingy/tidy.py +160 -0
  58. skilleter_thingy/trimpath.py +84 -0
  59. skilleter_thingy/window_rename.py +92 -0
  60. skilleter_thingy/xchmod.py +125 -0
  61. skilleter_thingy/yamlcheck.py +89 -0
  62. skilleter_thingy-0.0.22.dist-info/LICENSE +619 -0
  63. skilleter_thingy-0.0.22.dist-info/METADATA +22 -0
  64. skilleter_thingy-0.0.22.dist-info/RECORD +67 -0
  65. skilleter_thingy-0.0.22.dist-info/WHEEL +5 -0
  66. skilleter_thingy-0.0.22.dist-info/entry_points.txt +43 -0
  67. skilleter_thingy-0.0.22.dist-info/top_level.txt +1 -0
@@ -0,0 +1,274 @@
1
+ #! /usr/bin/env python3
2
+
3
+ """ Thingy gitprompt command
4
+
5
+ Copyright (C) 2017 John Skilleter
6
+
7
+ Used to create the portion of the shell prompt that optionally shows
8
+ the current git repo name and branch and to output a colour code indicating
9
+ the status of the current working tree.
10
+
11
+ Normally used in the shell setup scripts (e.g. ~/.bashrc) as:
12
+
13
+ export PS1=$(gitprompt OPTIONS)
14
+
15
+ Command line options:
16
+
17
+ '--colour'
18
+
19
+ Output a background colour code indicating the status of the
20
+ current tree, rather than the repo name and branch.
21
+
22
+ Colours used are:
23
+
24
+ Green - Clean repo, no local changes
25
+ Cyan - Clean repo with untracked file(s)
26
+ Yellow - Uncommitted local changes (added, copied or renamed files)
27
+ Red - Local changes that have not been added (files modified or deleted)
28
+ Magenta - Unmerged files
29
+
30
+ Other options are set via the Git configuration:
31
+
32
+ prompt.prefix: 0 - No prefix
33
+ 1 - Single letter indications of git status (untracked, modified, etc)
34
+ 2 - One word indications (untracked, modified, etc)
35
+
36
+ TODO: Limit the total prompt length more 'intelligently', rather than just bits of it.
37
+ TODO: Indicate whether current directory is writeable and/or put current owner in prompt if no the current user
38
+ """
39
+
40
+ ################################################################################
41
+
42
+ # Try and reduce the scope for an auto-repeating ^C to screw up the shell prompt
43
+
44
+ import signal
45
+
46
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
47
+
48
+ import os
49
+ import sys
50
+ import argparse
51
+
52
+ from skilleter_thingy import git
53
+ from skilleter_thingy import colour
54
+
55
+ ################################################################################
56
+ # Constants
57
+
58
+ # Prefix text used when showing git status in the prompt - first entry is the
59
+ # abbreviated form and the second is the verbose.
60
+
61
+ STATUS_PREFIX = \
62
+ {
63
+ 'untracked': ('u', 'untracked'),
64
+ 'added': ('A', 'added'),
65
+ 'modified': ('M', 'modified'),
66
+ 'unmerged': ('U', 'unmerged'),
67
+ 'deleted': ('D', 'deleted'),
68
+ 'copied': ('C', 'copied'),
69
+ 'renamed': ('R', 'renamed'),
70
+ 'stash': ('S', 'stashed')
71
+ }
72
+
73
+ MAX_BRANCH_NAME_LENGTH = int(os.environ.get('COLUMNS', 96)) / 3
74
+
75
+ ################################################################################
76
+
77
+ def write_colour_prompt(gitstate: dict):
78
+ """ Output the colour for the prompt, according to the current state of the
79
+ working tree. """
80
+
81
+ colour.write('[NORMAL]', newline=False)
82
+
83
+ if gitstate['modified'] or gitstate['deleted']:
84
+ colour.write('[REVERSE][RED]', newline=False)
85
+
86
+ elif gitstate['added'] or gitstate['copied'] or gitstate['renamed']:
87
+ colour.write('[REVERSE][YELLOW]', newline=False)
88
+
89
+ elif gitstate['unmerged']:
90
+ colour.write('[REVERSE][MAGENTA]', newline=False)
91
+
92
+ elif gitstate['untracked']:
93
+ colour.write('[REVERSE][CYAN]', newline=False)
94
+
95
+ elif gitstate['merging'] or gitstate['bisecting'] or gitstate['rebasing']:
96
+ colour.write('[REVERSE][BLACK]', newline=False)
97
+
98
+ else:
99
+ colour.write('[REVERSE][GREEN]', newline=False)
100
+
101
+ ################################################################################
102
+
103
+ def write_prompt_prefix(gitstate: dict):
104
+ """ Build a prompt prefix containing the type and number
105
+ of changes in the repo. """
106
+
107
+ prefix = []
108
+
109
+ # Get the status configuration from gitconfig and restrict it to the
110
+ # range 0..2
111
+
112
+ try:
113
+ status_prefix = int(git.config_get('prompt', 'prefix', defaultvalue='0'))
114
+ except ValueError:
115
+ status_prefix = 0
116
+ else:
117
+ status_prefix = max(min(status_prefix, 2), 0)
118
+
119
+ # Only output the status information if the prefix is non-zero
120
+
121
+ if status_prefix > 0:
122
+ i = status_prefix - 1
123
+
124
+ stashed = len(git.stash())
125
+
126
+ if stashed:
127
+ prefix.append('%s:%d' % (STATUS_PREFIX['stash'][i], stashed))
128
+
129
+ if gitstate['untracked']:
130
+ prefix.append('%s:%d' % (STATUS_PREFIX['untracked'][i], gitstate['untracked']))
131
+
132
+ if gitstate['added']:
133
+ prefix.append('%s:%d' % (STATUS_PREFIX['added'][i], gitstate['added']))
134
+
135
+ if gitstate['modified']:
136
+ prefix.append('%s:%d' % (STATUS_PREFIX['modified'][i], gitstate['modified']))
137
+
138
+ if gitstate['unmerged']:
139
+ prefix.append('%s:%d' % (STATUS_PREFIX['unmerged'][i], gitstate['unmerged']))
140
+
141
+ if gitstate['deleted']:
142
+ prefix.append('%s:%d' % (STATUS_PREFIX['deleted'][i], gitstate['deleted']))
143
+
144
+ if gitstate['copied']:
145
+ prefix.append('%s:%d' % (STATUS_PREFIX['copied'][i], gitstate['copied']))
146
+
147
+ if gitstate['renamed']:
148
+ prefix.append('%s:%d' % (STATUS_PREFIX['renamed'][i], gitstate['renamed']))
149
+
150
+ # Get the current branch, tag or commit
151
+
152
+ branch = git.branch() or git.tag() or git.current_commit(short=True)
153
+
154
+ # TODO: More intelligent branch name pruning - currently just trims it if longer than limit
155
+ # TODO: Keep branch name up to minimum characters long - use more components if still shorter
156
+
157
+ if len(branch) > MAX_BRANCH_NAME_LENGTH:
158
+ truncated_name = None
159
+
160
+ for sep in (' ', '-', '_', '/'):
161
+ shortname = sep.join(branch.split(sep)[0:2])
162
+ if (truncated_name and len(truncated_name) > len(shortname)) or not truncated_name:
163
+ truncated_name = shortname
164
+
165
+ if truncated_name:
166
+ branch = '%s...' % truncated_name
167
+
168
+ if gitstate['rebasing']:
169
+ prefix.append('(rebasing)')
170
+ elif gitstate['bisecting']:
171
+ prefix.append('(bisecting)')
172
+ elif gitstate['merging']:
173
+ prefix.append('(merging)')
174
+
175
+ if prefix:
176
+ sys.stdout.write(' {%s}' % ' '.join(prefix))
177
+
178
+ project = git.project(short=True)
179
+
180
+ if project:
181
+ sys.stdout.write(f' {project}: {branch} ')
182
+ else:
183
+ sys.stdout.write(f' {branch} ')
184
+
185
+ ################################################################################
186
+
187
+ def git_status(colour_output: str, prompt_output: str):
188
+ """ Catalogue the current state of the working tree then call the function
189
+ to either output a suitable colour or the prompt text or both """
190
+
191
+ # Get the working tree, just return if there's an error
192
+
193
+ try:
194
+ working_tree = git.working_tree()
195
+ except git.GitError:
196
+ return
197
+
198
+ # Return if we are not in a working tree
199
+
200
+ if not working_tree:
201
+ return
202
+
203
+ # gitstate contains counters for numbers of modified (etc.) elements in the tree and flags to indicate
204
+ # whether we're currently in a merge/rebase/bisect state.
205
+
206
+ gitstate = {'modified': 0, 'added': 0, 'untracked': 0, 'unmerged': 0, 'deleted': 0, 'renamed': 0, 'copied': 0}
207
+
208
+ # Set flags if we are currently merging/rebasing/bisecting and get the current status
209
+
210
+ try:
211
+ gitstate['merging'] = git.merging()
212
+ gitstate['rebasing'] = git.rebasing()
213
+ gitstate['bisecting'] = git.bisecting()
214
+
215
+ status = git.status()
216
+ except git.GitError as exc:
217
+ # Major failure of gitness - report the error and quit
218
+
219
+ if colour_output:
220
+ colour.write('[WHITE][BRED]', newline=False)
221
+ else:
222
+ colour.write(' ERROR: %s ' % exc.msg.split('\n')[0], newline=False)
223
+
224
+ return
225
+
226
+ # Count the number of files in each state
227
+
228
+ for st in status:
229
+ gitstate['untracked'] += '?' in st[0]
230
+ gitstate['added'] += 'A' in st[0]
231
+ gitstate['modified'] += 'M' in st[0]
232
+ gitstate['unmerged'] += 'U' in st[0]
233
+ gitstate['deleted'] += 'D' in st[0]
234
+ gitstate['copied'] += 'C' in st[0]
235
+ gitstate['renamed'] += 'R' in st[0]
236
+
237
+ # Set the output colour or output the prompt prefix
238
+
239
+ if colour_output:
240
+ write_colour_prompt(gitstate)
241
+
242
+ if prompt_output or not colour_output:
243
+ write_prompt_prefix(gitstate)
244
+
245
+ ################################################################################
246
+
247
+ def main():
248
+ """ Parse the command line and output colour or status """
249
+
250
+ parser = argparse.ArgumentParser(description='Report current branch and, optionally, git repo name to be embedded in shell prompt')
251
+ parser.add_argument('--colour', action='store_true', help='Output colour code indicating working tree status')
252
+ parser.add_argument('--prompt', action='store_true', help='Output the prompt (default if --colour not specified)')
253
+
254
+ args = parser.parse_args()
255
+
256
+ git_status(args.colour, args.prompt)
257
+
258
+ ################################################################################
259
+
260
+ def gitprompt():
261
+ """Entry point"""
262
+
263
+ try:
264
+ main()
265
+
266
+ except KeyboardInterrupt:
267
+ sys.exit(1)
268
+ except BrokenPipeError:
269
+ sys.exit(2)
270
+
271
+ ################################################################################
272
+
273
+ if __name__ == '__main__':
274
+ gitprompt()
skilleter_thingy/gl.py ADDED
@@ -0,0 +1,174 @@
1
+ #! /usr/bin/env python3
2
+
3
+ """ Nearly MVP of a command to do things to GitLab
4
+ Currently just implements the 'mr-list' command which outputs a list
5
+ of merge requests from all projects in CSV format.
6
+
7
+ TODO: Lots and lots of things! """
8
+
9
+ ################################################################################
10
+
11
+ import argparse
12
+ import os
13
+ import sys
14
+ from collections import defaultdict
15
+
16
+ from skilleter_thingy import colour
17
+ from skilleter_thingy import gitlab
18
+
19
+ ################################################################################
20
+
21
+ def mr_list(args):
22
+ """ List merge requests """
23
+
24
+ gl = gitlab.GitLab(args.server)
25
+
26
+ # TODO: Could incorporate some/all filtering in the request rather than getting all MRs and filtering them
27
+
28
+ mrs = gl.merge_requests(scope='all')
29
+
30
+ # TODO: Output format other than CSV
31
+ # TODO: More filtering
32
+
33
+ if args.summary:
34
+ authors = defaultdict(int)
35
+ reviewers = defaultdict(int)
36
+ combos = defaultdict(int)
37
+
38
+ count = 0
39
+ for mr in mrs:
40
+ author = mr['author']['username']
41
+ authors[author] += 1
42
+
43
+ if mr['state'] == 'merged':
44
+ try:
45
+ reviewer = mr['merged_by']['username']
46
+ except TypeError:
47
+ reviewer = 'UNKNOWN'
48
+
49
+ reviewers[reviewer] += 1
50
+ combos[f"{author}|{reviewer}"] += 1
51
+
52
+ count += 1
53
+ if args.limit and count > args.limit:
54
+ break
55
+
56
+ print('Number of merge requests by author')
57
+
58
+ for value in sorted(set(authors.values()), reverse=True):
59
+ for person in authors:
60
+ if authors[person] == value:
61
+ print(f' {person:32}: {authors[person]}')
62
+
63
+ print()
64
+ print('Number of merge requests by reviewer')
65
+
66
+ for value in sorted(set(reviewers.values()), reverse=True):
67
+ for person in reviewers:
68
+ if reviewers[person] == value:
69
+ print(f' {person:32}: {reviewers[person]}')
70
+
71
+ print()
72
+ print('Author/Reviewer combinations for merged changes')
73
+
74
+ for value in sorted(set(combos.values()), reverse=True):
75
+ for combo in combos:
76
+ if combos[combo] == value:
77
+ author, reviewer = combo.split('|')
78
+
79
+ print(f' Written by {author}, reviewed by {reviewer}: {combos[combo]}')
80
+
81
+ else:
82
+ print('state,merge id,project id,author,approver,title,merge date')
83
+
84
+ for mr in mrs:
85
+ if args.author and mr['author']['username'] != args.author:
86
+ continue
87
+
88
+ if mr['state'] == 'merged':
89
+ try:
90
+ merged_by = mr['merged_by']['username']
91
+ except TypeError:
92
+ merged_by = 'NONE'
93
+
94
+ if args.approver and merged_by != args.approver:
95
+ continue
96
+
97
+ if not args.summary:
98
+ print('%s,%s,%s,%s,%s,%s,"%s"' % (mr['state'], mr['id'], mr['project_id'],
99
+ mr['author']['username'], merged_by, mr['title'], mr['merged_at']))
100
+ elif args.all and not args.summary:
101
+ print('%s,%s,%s,%s,,"%s",' % (mr['state'], mr['id'], mr['project_id'], mr['author']['username'], mr['title']))
102
+
103
+ count += 1
104
+ if args.limit and count > args.limit:
105
+ break
106
+
107
+ ################################################################################
108
+
109
+ def main():
110
+ """ Entry point """
111
+
112
+ parser = argparse.ArgumentParser(description='Gitlab commands')
113
+
114
+ parser.add_argument('--dryrun', '--dry-run', '-D', action='store_true', help='Dry-run comands')
115
+ parser.add_argument('--debug', '-d', action='store_true', help='Debug')
116
+ parser.add_argument('--verbose', '-v', action='store_true', help='Verbosity to the maximum')
117
+ parser.add_argument('--server', '-s', default=None, help='The GitLab server')
118
+ parser.add_argument('--token', '-t', default=None, help='The GitLab access token')
119
+
120
+ subparsers = parser.add_subparsers(dest='command')
121
+
122
+ parser_mr_list = subparsers.add_parser('mr-list', help='List merge requests')
123
+ parser_mr_list.add_argument('--all', action='store_true', help='List un-merged merge requests')
124
+ parser_mr_list.add_argument('--author', action='store', help='List merge requests created by a specific user')
125
+ parser_mr_list.add_argument('--approver', action='store', help='List merge requests approved by a specific user')
126
+ parser_mr_list.add_argument('--summary', action='store_true', help='Produce a summary report')
127
+ parser_mr_list.add_argument('--limit', action='store', type=int, help='Output the first N merge requests')
128
+
129
+ # TODO: Other subcommands
130
+
131
+ # Parse the command line
132
+
133
+ args = parser.parse_args()
134
+
135
+ # Check the server/token configuration
136
+
137
+ if not args.server:
138
+ args.server = os.environ.get('GITLAB_SERVER', None)
139
+
140
+ if not args.server:
141
+ colour.error('The GitLab server must be specified on the command line or via the [BLUE:GITLAB_SERVER] environment variable')
142
+
143
+ if not args.token:
144
+ args.token = os.environ.get('GITLAB_TOKEN', None)
145
+
146
+ if not args.token:
147
+ colour.error('GitLab access token must be specified on the command line or via the [BLUE:GITLAB_TOKEN] environment variable')
148
+
149
+ # Invoke the subcommand
150
+
151
+ if args.command == 'mr-list':
152
+ mr_list(args)
153
+
154
+ elif not args.command:
155
+ colour.error('No command specified')
156
+ else:
157
+ colour.error(f'Invalid command: "{args.command}"')
158
+
159
+ ################################################################################
160
+
161
+ def gl():
162
+ """Entry point"""
163
+
164
+ try:
165
+ main()
166
+ except KeyboardInterrupt:
167
+ sys.exit(1)
168
+ except BrokenPipeError:
169
+ sys.exit(2)
170
+
171
+ ################################################################################
172
+
173
+ if __name__ == '__main__':
174
+ gl()