skilleter-thingy 0.2.0__tar.gz → 0.2.1__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.

Potentially problematic release.


This version of skilleter-thingy might be problematic. Click here for more details.

Files changed (74) hide show
  1. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/PKG-INFO +46 -1
  2. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/README.md +45 -0
  3. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/pyproject.toml +10 -1
  4. skilleter_thingy-0.2.1/skilleter_thingy/borger.py +273 -0
  5. skilleter_thingy-0.2.1/skilleter_thingy/diskspacecheck.py +67 -0
  6. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/ggit.py +1 -0
  7. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/ggrep.py +1 -0
  8. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_br.py +7 -0
  9. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_ca.py +8 -0
  10. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_cleanup.py +11 -0
  11. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_co.py +8 -3
  12. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_common.py +12 -4
  13. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_hold.py +9 -0
  14. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_mr.py +11 -0
  15. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_parent.py +23 -18
  16. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_retag.py +10 -0
  17. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_review.py +1 -0
  18. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_update.py +1 -0
  19. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/git_wt.py +2 -0
  20. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/gitprompt.py +1 -0
  21. skilleter_thingy-0.2.1/skilleter_thingy/localphotosync.py +201 -0
  22. skilleter_thingy-0.2.1/skilleter_thingy/moviemover.py +133 -0
  23. skilleter_thingy-0.2.1/skilleter_thingy/photodupe.py +135 -0
  24. skilleter_thingy-0.2.1/skilleter_thingy/phototidier.py +248 -0
  25. skilleter_thingy-0.2.1/skilleter_thingy/splitpics.py +99 -0
  26. skilleter_thingy-0.2.1/skilleter_thingy/sysmon.py +435 -0
  27. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/git.py +18 -5
  28. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/git2.py +20 -7
  29. skilleter_thingy-0.2.1/skilleter_thingy/window_rename.py +92 -0
  30. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy.egg-info/PKG-INFO +46 -1
  31. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy.egg-info/SOURCES.txt +9 -0
  32. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy.egg-info/entry_points.txt +9 -0
  33. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/LICENSE +0 -0
  34. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/setup.cfg +0 -0
  35. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/__init__.py +0 -0
  36. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/addpath.py +0 -0
  37. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/console_colours.py +0 -0
  38. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/docker_purge.py +0 -0
  39. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/ffind.py +0 -0
  40. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/gitcmp_helper.py +0 -0
  41. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/gl.py +0 -0
  42. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/linecount.py +0 -0
  43. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/multigit.py +0 -0
  44. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/py_audit.py +0 -0
  45. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/readable.py +0 -0
  46. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/remdir.py +0 -0
  47. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/rmdupe.py +0 -0
  48. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/rpylint.py +0 -0
  49. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/strreplace.py +0 -0
  50. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/tfm.py +0 -0
  51. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/tfparse.py +0 -0
  52. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/__init__.py +0 -0
  53. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/colour.py +0 -0
  54. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/dc_curses.py +0 -0
  55. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/dc_defaults.py +0 -0
  56. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/dc_util.py +0 -0
  57. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/dircolors.py +0 -0
  58. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/docker.py +0 -0
  59. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/files.py +0 -0
  60. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/gitlab.py +0 -0
  61. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/path.py +0 -0
  62. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/popup.py +0 -0
  63. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/process.py +0 -0
  64. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/run.py +0 -0
  65. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/tfm_pane.py +0 -0
  66. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/tidy.py +0 -0
  67. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/thingy/venv_template.py +0 -0
  68. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/trimpath.py +0 -0
  69. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/venv_create.py +0 -0
  70. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/xchmod.py +0 -0
  71. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy/yamlcheck.py +0 -0
  72. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy.egg-info/dependency_links.txt +0 -0
  73. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy.egg-info/requires.txt +0 -0
  74. {skilleter_thingy-0.2.0 → skilleter_thingy-0.2.1}/skilleter_thingy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skilleter_thingy
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: A collection of useful utilities, mainly aimed at making Git more friendly
5
5
  Author-email: John Skilleter <john@skilleter.org.uk>
6
6
  Project-URL: Home, https://skilleter.org.uk
@@ -597,10 +597,55 @@ YAML validator - checks that a file is valid YAML (use yamllint to verify that i
597
597
 
598
598
  These will be moved to the skilleter-extras package in due course.
599
599
 
600
+ ## borger
601
+
602
+ Wrapper for the borg backup utility to make it easier to use with a fixed set of options.
603
+
600
604
  ## consolecolours
601
605
 
602
606
  Display all available colours in the console.
603
607
 
608
+ ## diskspacecheck
609
+
610
+ Check how much free space is available on all filesystems, ignoring read-only filesystems, /dev and tmpfs.
611
+
612
+ Issue a warning if any are above 90% used.
613
+
614
+ ## gphotosync & localphotosync
615
+
616
+ Utilities for syncing photos from Google Photos or a local directory to local storage
617
+
618
+ ## moviemover
619
+
620
+ Search for files matching a wildcard in a directory tree and move them to an equivalent location in a different tree
621
+
622
+ ## phototidier
623
+
624
+ Perform various tidying operations on a directory full of photos:
625
+
626
+ * Remove leading '$' and '_' from filenames
627
+ * Move files in hidden directories up 1 level
628
+ * If the EXIF data in a photo indicates that it was taken on date that doesn't match the name of the directory it is stored in (in YYYY-MM-DD format) then it is moved to the correct directory, creating it if necessary.
629
+
630
+ All move/rename operations are carried out safely with the file being moved having
631
+ a numeric suffix added to the name if it conflicts with an existing file.
632
+
633
+ ## photodupe
634
+
635
+ Search for duplicate images in a directory tree
636
+
637
+ ## splitpics
638
+
639
+ Copy a directory full of pictures to a destination, creating subdiretories with a fixed number of pictures in each in the destination directory for use with FAT filesystems and digital photo frames.
640
+
641
+ ## sysmon
642
+
643
+ Simple console system monitor
644
+
645
+ ## window-rename
646
+
647
+ Monitor window titles and rename them to fit an alphabetical grouping in 'Appname - Document' format.
648
+
604
649
  # Obsolescent Commands
605
650
 
606
651
  These commands will probably be retired in future versions of Thingy
@@ -574,10 +574,55 @@ YAML validator - checks that a file is valid YAML (use yamllint to verify that i
574
574
 
575
575
  These will be moved to the skilleter-extras package in due course.
576
576
 
577
+ ## borger
578
+
579
+ Wrapper for the borg backup utility to make it easier to use with a fixed set of options.
580
+
577
581
  ## consolecolours
578
582
 
579
583
  Display all available colours in the console.
580
584
 
585
+ ## diskspacecheck
586
+
587
+ Check how much free space is available on all filesystems, ignoring read-only filesystems, /dev and tmpfs.
588
+
589
+ Issue a warning if any are above 90% used.
590
+
591
+ ## gphotosync & localphotosync
592
+
593
+ Utilities for syncing photos from Google Photos or a local directory to local storage
594
+
595
+ ## moviemover
596
+
597
+ Search for files matching a wildcard in a directory tree and move them to an equivalent location in a different tree
598
+
599
+ ## phototidier
600
+
601
+ Perform various tidying operations on a directory full of photos:
602
+
603
+ * Remove leading '$' and '_' from filenames
604
+ * Move files in hidden directories up 1 level
605
+ * If the EXIF data in a photo indicates that it was taken on date that doesn't match the name of the directory it is stored in (in YYYY-MM-DD format) then it is moved to the correct directory, creating it if necessary.
606
+
607
+ All move/rename operations are carried out safely with the file being moved having
608
+ a numeric suffix added to the name if it conflicts with an existing file.
609
+
610
+ ## photodupe
611
+
612
+ Search for duplicate images in a directory tree
613
+
614
+ ## splitpics
615
+
616
+ Copy a directory full of pictures to a destination, creating subdiretories with a fixed number of pictures in each in the destination directory for use with FAT filesystems and digital photo frames.
617
+
618
+ ## sysmon
619
+
620
+ Simple console system monitor
621
+
622
+ ## window-rename
623
+
624
+ Monitor window titles and rename them to fit an alphabetical grouping in 'Appname - Document' format.
625
+
581
626
  # Obsolescent Commands
582
627
 
583
628
  These commands will probably be retired in future versions of Thingy
@@ -7,7 +7,7 @@ name = "skilleter_thingy"
7
7
 
8
8
  # Version must be incremented to install updated Thingy
9
9
 
10
- version = "0.2.0"
10
+ version = "0.2.1"
11
11
 
12
12
  authors = [
13
13
  {name="John Skilleter", email="john@skilleter.org.uk"},
@@ -42,7 +42,9 @@ Home = "https://skilleter.org.uk"
42
42
 
43
43
  [project.scripts]
44
44
  addpath = "skilleter_thingy:addpath.addpath"
45
+ borger = "skilleter_thingy:borger.borger"
45
46
  consolecolours = "skilleter_thingy:console_colours.console_colours"
47
+ diskspacecheck = "skilleter_thingy:diskspacecheck.diskspacecheck"
46
48
  docker-purge = "skilleter_thingy:docker_purge.docker_purge"
47
49
  ffind = "skilleter_thingy:ffind.ffind"
48
50
  ggit = "skilleter_thingy:ggit.ggit"
@@ -64,19 +66,26 @@ gitprompt = "skilleter_thingy:gitprompt.gitprompt"
64
66
  gl = "skilleter_thingy:gl.gl"
65
67
  gphotosync = "skilleter_thingy:gphotosync.gphotosync"
66
68
  linecount = "skilleter_thingy:linecount.linecount"
69
+ localphotosync = "skilleter_thingy:localphotosync.localphotosync"
67
70
  mg = "skilleter_thingy:mg.mg"
71
+ moviemover = "skilleter_thingy:moviemover.moviemover"
68
72
  multigit = "skilleter_thingy:multigit.multigit"
73
+ photodupe = "skilleter_thingy:photodupe.photodupe"
74
+ phototidier = "skilleter_thingy:phototidier.phototidier"
69
75
  py-audit = "skilleter_thingy:py_audit.py_audit"
70
76
  readable = "skilleter_thingy:readable.readable"
71
77
  remdir = "skilleter_thingy:remdir.remdir"
72
78
  rmdupe = "skilleter_thingy:rmdupe.rmdupe"
73
79
  rpylint = "skilleter_thingy:rpylint.rpylint"
74
80
  s3-sync = "skilleter_thingy:s3_sync.s3_sync"
81
+ splitpics = "skilleter_thingy:splitpics.splitpics"
75
82
  strreplace = "skilleter_thingy:strreplace.strreplace"
83
+ sysmon = "skilleter_thingy:sysmon.sysmon"
76
84
  tfm = "skilleter_thingy:tfm.tfm"
77
85
  tfparse = "skilleter_thingy:tfparse.tfparse"
78
86
  trimpath = "skilleter_thingy:trimpath.trimpath"
79
87
  venv-create = "skilleter_thingy:venv_create.venv_create"
80
88
  webwatch = "skilleter_thingy:webwatch.webwatch"
89
+ window-rename = "skilleter_thingy:window_rename.window_rename"
81
90
  xchmod = "skilleter_thingy:xchmod.xchmod"
82
91
  yamlcheck = "skilleter_thingy:yamlcheck.yamlcheck"
@@ -0,0 +1,273 @@
1
+ #! /usr/bin/env python3
2
+
3
+ """
4
+ Wrapper for the borg backup command
5
+
6
+ Copyright (C) 2018 John Skilleter
7
+
8
+ TODO: Major tidy-up as this is a translation of a Bash script.
9
+ TODO: Merge with the usb-backup script since both do almost the same job
10
+ TODO: Default configuration file should be named for the hostname
11
+ TODO: Move all configuration data into the configuration file
12
+ """
13
+
14
+ ################################################################################
15
+ # Imports
16
+
17
+ import sys
18
+ import os
19
+ import time
20
+ import argparse
21
+ import configparser
22
+ import subprocess
23
+ from pathlib import Path
24
+
25
+ ################################################################################
26
+ # Variables
27
+
28
+ DEFAULT_CONFIG_FILE = Path('borger.ini')
29
+
30
+ COMMANDS = ('backup', 'mount', 'umount', 'compact', 'info', 'prune', 'check', 'init')
31
+
32
+ # TODO: NOT USED
33
+ PRUNE_OPTIONS = [
34
+ '--keep-within', '7d',
35
+ '--keep-daily', '30',
36
+ '--keep-weekly', '26',
37
+ '--keep-monthly', '24',
38
+ '--keep-yearly', '10',
39
+ ]
40
+
41
+ ################################################################################
42
+
43
+ def run(args, cmd):
44
+ """Run a subprocess."""
45
+
46
+ if args.debug:
47
+ cmd_str = ' '.join(cmd)
48
+ print(f'Running "{cmd_str}"')
49
+
50
+ try:
51
+ return subprocess.run(cmd, check=True)
52
+ except FileNotFoundError:
53
+ print('Borg backup is not installed')
54
+ sys.exit(1)
55
+
56
+ ################################################################################
57
+
58
+ def borg_backup(args, exclude_list):
59
+ """Perform a backup."""
60
+
61
+ create_options = ['--compression', 'auto,lzma']
62
+
63
+ version = time.strftime('%Y-%m-%d-%H:%M:%S')
64
+
65
+ print(f'Creating backup version {version}')
66
+
67
+ if args.verbose:
68
+ create_options += ['--list', '--filter=AMC']
69
+
70
+ if args.dryrun:
71
+ create_options.append('--dry-run')
72
+ else:
73
+ create_options.append('--stats')
74
+
75
+ exclude_opts = []
76
+
77
+ if exclude_list:
78
+ for exclude in exclude_list:
79
+ exclude_opts += ['--exclude', exclude]
80
+
81
+ os.chdir(args.source)
82
+
83
+ run(args,
84
+ ['borg'] + args.options + ['create', f'{str(args.destination)}::{version}', str(args.source)] + create_options +
85
+ ['--show-rc', '--one-file-system', '--exclude-caches'] + exclude_opts)
86
+
87
+ ################################################################################
88
+
89
+ def borg_prune(args):
90
+ """Prune the repo by limiting the number of backups stored."""
91
+
92
+ print('Pruning old backups')
93
+
94
+ # Keep all backups for at least 7 days, 1 per day for 30 days, 1 per week for 2 years
95
+ # 1 per month for 4 years and 1 per year for 10 years.
96
+
97
+ run(args, ['borg'] + args.options + ['prune', str(args.destination)] + PRUNE_OPTIONS)
98
+
99
+ ################################################################################
100
+
101
+ def borg_compact(args):
102
+ """Compact the repo."""
103
+
104
+ print('Compacting the backup')
105
+
106
+ # Keep all backups for at least 7 days, 1 per day for 30 days, 1 per week for 2 years
107
+ # 1 per month for 4 years and 1 per year for 10 years.
108
+
109
+ run(args, ['borg'] + args.options + ['compact', str(args.destination)])
110
+
111
+ ################################################################################
112
+
113
+ def borg_info(args):
114
+ """Info."""
115
+
116
+ run(args, ['borg'] + args.options + ['info', str(args.destination)])
117
+
118
+ ################################################################################
119
+
120
+ def borg_mount(args):
121
+ """Mount."""
122
+
123
+ print(f'Mounting Borg backups at {args.mount_dir}')
124
+
125
+ mount = Path(args.mount_dir)
126
+
127
+ if not mount.is_dir():
128
+ mount.mkdir()
129
+
130
+ run(args, ['borg'] + args.options + ['mount', str(args.destination), str(mount)])
131
+
132
+ ################################################################################
133
+
134
+ def borg_umount(args):
135
+ """Unmount."""
136
+
137
+ print('Unmounting {args.mount}')
138
+
139
+ run(args, ['borg'] + args.options + ['umount', str(args.mount)])
140
+
141
+ ################################################################################
142
+
143
+ def borg_check(args):
144
+ """Check the status of a backup."""
145
+
146
+ run(args, ['borg'] + args.options + ['check', str(args.destination)])
147
+
148
+ ################################################################################
149
+
150
+ def borg_init(args):
151
+ """Initialise a backup."""
152
+
153
+ run(args, ['borg'] + args.options + ['init', str(args.destination), '--encryption=none'])
154
+
155
+ ################################################################################
156
+
157
+ def process_excludes(exclude_data):
158
+ """Process the include list from the configuration file."""
159
+
160
+ return exclude_data.replace('%', str(Path.cwd())).split('\n')
161
+
162
+ ################################################################################
163
+
164
+ def main():
165
+ """Entry point."""
166
+
167
+ command_list = ', '.join(COMMANDS)
168
+
169
+ parser = argparse.ArgumentParser(description='Wrapper app for Borg backup to make it easier to use')
170
+ parser.add_argument('--dryrun', '--dry-run', '-D', action='store_true', help='Dry-run comands')
171
+ parser.add_argument('--debug', '-d', action='store_true', help='Debug')
172
+ parser.add_argument('--verbose', '-v', action='store_true', help='Verbosity to the maximum')
173
+ parser.add_argument('--config', '-c', default=None, help='Specify the configuration file')
174
+ parser.add_argument('commands', nargs='+', help=f'One or more commands ({command_list})')
175
+ args = parser.parse_args()
176
+
177
+ # If no config file specified then look in all the usual places
178
+
179
+ if args.config:
180
+ args.config = Path(args.config)
181
+ elif DEFAULT_CONFIG_FILE.is_file():
182
+ args.config = DEFAULT_CONFIG_FILE
183
+ else:
184
+ args.config = Path.home() / DEFAULT_CONFIG_FILE
185
+
186
+ if not args.config.is_file():
187
+ args.config = Path(sys.argv[0]).parent / DEFAULT_CONFIG_FILE
188
+
189
+ # Check that the configuration file exists
190
+
191
+ if not args.config.is_file():
192
+ print(f'Configuration file "{args.config}" not found')
193
+ sys.exit(1)
194
+
195
+ # Default options
196
+
197
+ args.options = []
198
+
199
+ # Read the configuration file
200
+
201
+ config = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
202
+ config.read(args.config)
203
+
204
+ if 'borger' not in config:
205
+ print('Invalid configuration file "args.config"')
206
+ sys.exit(1)
207
+
208
+ exclude = process_excludes(config['borger']['exclude']) if 'exclude' in config['borger'] else []
209
+
210
+ if 'prune' in config['borger']:
211
+ # TODO: Stuff
212
+ print('Parser for the prune option is not implemented yet')
213
+ sys.exit(1)
214
+
215
+ if 'destination' in config['borger']:
216
+ args.destination = config['borger']['destination']
217
+ else:
218
+ print('Destination directory not specified')
219
+ sys.exit(1)
220
+
221
+ if 'source' in config['borger']:
222
+ args.source = Path(config['borger']['source'])
223
+ else:
224
+ print('Source directory not specified')
225
+ sys.exit(1)
226
+
227
+ # Initialise if necessary
228
+
229
+ if args.debug:
230
+ args.options.append('--verbose')
231
+
232
+ if args.verbose:
233
+ args.options.append('--progress')
234
+
235
+ # Decide what to do
236
+
237
+ for command in args.commands:
238
+ if command == 'backup':
239
+ borg_backup(args, exclude)
240
+ elif command == 'mount':
241
+ borg_mount(args)
242
+ elif command == 'umount':
243
+ borg_umount(args)
244
+ elif command == 'info':
245
+ borg_info(args)
246
+ elif command == 'prune':
247
+ borg_prune(args)
248
+ elif command == 'check':
249
+ borg_check(args)
250
+ elif command == 'init':
251
+ borg_init(args)
252
+ elif command == 'compact':
253
+ borg_compact(args)
254
+ else:
255
+ print(f'Unrecognized command: {command}')
256
+ sys.exit(2)
257
+
258
+ ################################################################################
259
+
260
+ def borger():
261
+ """Entry point"""
262
+
263
+ try:
264
+ main()
265
+ except KeyboardInterrupt:
266
+ sys.exit(1)
267
+ except BrokenPipeError:
268
+ sys.exit(2)
269
+
270
+ ################################################################################
271
+
272
+ if __name__ == '__main__':
273
+ borger()
@@ -0,0 +1,67 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """ Check how much free space is available on all filesystems, ignoring
5
+ read-only filesystems, /dev and tmpfs.
6
+
7
+ Issue a warning if any are above 90% used.
8
+ """
9
+ ################################################################################
10
+
11
+ import sys
12
+ import argparse
13
+ import psutil
14
+
15
+ ################################################################################
16
+
17
+ WARNING_LEVEL = 15
18
+
19
+ ################################################################################
20
+
21
+ def main():
22
+ """ Do everything """
23
+
24
+ parser = argparse.ArgumentParser(description='Check for filesystems that are running low on space')
25
+ parser.add_argument('--level', action='store', type=int, default=WARNING_LEVEL,
26
+ help='Warning if less than this amount of space is available on any writeable, mounted filesystem (default=%d)' % WARNING_LEVEL)
27
+ args = parser.parse_args()
28
+
29
+ if args.level < 0 or args.level > 100:
30
+ print('Invalid value: %d' % args.level)
31
+ sys.exit(3)
32
+
33
+ disks = psutil.disk_partitions()
34
+ devices = []
35
+ warning = []
36
+
37
+ for disk in disks:
38
+ if 'ro' not in disk.opts.split(',') and disk.device not in devices:
39
+ devices.append(disk.device)
40
+ usage = psutil.disk_usage(disk.mountpoint)
41
+
42
+ disk_space = 100 - usage.percent
43
+
44
+ if disk_space < args.level:
45
+ warning.append('%s has only %2.1f%% space available' % (disk.mountpoint, disk_space))
46
+
47
+ if warning:
48
+ print('Filesystems with less than %d%% available space:' % args.level)
49
+ print('\n'.join(warning))
50
+
51
+ ################################################################################
52
+
53
+ def diskspacecheck():
54
+ """Entry point"""
55
+
56
+ try:
57
+ main()
58
+
59
+ except KeyboardInterrupt:
60
+ sys.exit(1)
61
+ except BrokenPipeError:
62
+ sys.exit(2)
63
+
64
+ ################################################################################
65
+
66
+ if __name__ == '__main__':
67
+ diskspacecheck()
@@ -7,6 +7,7 @@ import os
7
7
  import subprocess
8
8
  import argparse
9
9
 
10
+ # TODO: Update to git2
10
11
  import thingy.git as git
11
12
  import thingy.colour as colour
12
13
 
@@ -14,6 +14,7 @@ import sys
14
14
  import argparse
15
15
 
16
16
  import thingy.colour as colour
17
+ # TODO: Update to git2
17
18
  import thingy.git as git
18
19
 
19
20
  ################################################################################
@@ -13,6 +13,7 @@
13
13
  """
14
14
  ################################################################################
15
15
 
16
+ import os
16
17
  import sys
17
18
  import argparse
18
19
  import fnmatch
@@ -21,6 +22,7 @@ import datetime
21
22
  from dateutil.parser import parse
22
23
  from dateutil.relativedelta import relativedelta
23
24
 
25
+ # TODO: Update to git2
24
26
  import thingy.git as git
25
27
  import thingy.colour as colour
26
28
 
@@ -34,10 +36,15 @@ def parse_command_line():
34
36
  parser.add_argument('--all', '-a', action='store_true', help='List all branches, including remotes')
35
37
  parser.add_argument('--delete', '-d', action='store_true',
36
38
  help='Delete the specified branch(es), even if it is the current one (list of branches to delete must be supplied as parameters)')
39
+ parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
40
+ help='Run the command in the specified directory')
37
41
  parser.add_argument('branches', nargs='*', help='Filter the list of branches according to one or more patterns')
38
42
 
39
43
  args = parser.parse_args()
40
44
 
45
+ if args.path:
46
+ os.chdir(args.path[0])
47
+
41
48
  if args.delete and not args.branches:
42
49
  colour.error('You must specify the branches to delete', prefix=True)
43
50
 
@@ -18,6 +18,7 @@ import sys
18
18
  import logging
19
19
 
20
20
  import thingy.colour as colour
21
+ # TODO: Update to git2
21
22
  import thingy.git as git
22
23
 
23
24
  ################################################################################
@@ -43,6 +44,8 @@ def main():
43
44
  parser.add_argument('--patch', '-p', action='store_true', help='Use the interactive patch selection interface to chose which changes to commit.')
44
45
  parser.add_argument('--verbose', '-v', action='store_true', help='Verbose mode')
45
46
  parser.add_argument('--dry-run', '-D', action='store_true', help='Dry-run')
47
+ parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
48
+ help='Run the command in the specified directory')
46
49
 
47
50
  parser.add_argument('files', nargs='*', help='List of files to add to the commit')
48
51
 
@@ -54,6 +57,11 @@ def main():
54
57
  logging.basicConfig(level=logging.INFO)
55
58
  logging.info('Debug logging enabled')
56
59
 
60
+ # Change directory, if specified
61
+
62
+ if args.path:
63
+ os.chdir(args.path[0])
64
+
57
65
  # 'Add' implies 'all'
58
66
 
59
67
  if args.everything:
@@ -9,10 +9,12 @@
9
9
  """
10
10
  ################################################################################
11
11
 
12
+ import os
12
13
  import sys
13
14
  import argparse
14
15
  import logging
15
16
 
17
+ # TODO: Update to git2
16
18
  import thingy.git as git
17
19
  import thingy.colour as colour
18
20
 
@@ -38,6 +40,8 @@ def parse_command_line():
38
40
  parser.add_argument('--unmerged', '-u', action='store_true', dest='list_unmerged', help='List branches that have NOT been merged')
39
41
  parser.add_argument('--yes', '-y', action='store_true', dest='force', help='Assume "yes" in response to any prompts (e.g. to delete branches)')
40
42
  parser.add_argument('--debug', action='store_true', help='Enable debug output')
43
+ parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
44
+ help='Run the command in the specified directory')
41
45
 
42
46
  parser.add_argument('branches', nargs='*', help='List of branches to check (default is all branches)')
43
47
 
@@ -94,6 +98,11 @@ def main():
94
98
  if args.debug:
95
99
  logging.basicConfig(level=logging.INFO)
96
100
 
101
+ # Change directory, if specified
102
+
103
+ if args.path:
104
+ os.chdir(args.path[0])
105
+
97
106
  # Get the list of all local branches
98
107
 
99
108
  try:
@@ -280,6 +289,8 @@ def git_cleanup():
280
289
  sys.exit(1)
281
290
  except BrokenPipeError:
282
291
  sys.exit(2)
292
+ except git.GitError as exc:
293
+ colour.error(exc.msg, status=exc.status, prefix=True)
283
294
 
284
295
  ################################################################################
285
296
 
@@ -20,6 +20,7 @@
20
20
  """
21
21
  ################################################################################
22
22
 
23
+ import os
23
24
  import logging
24
25
  import sys
25
26
  import argparse
@@ -68,6 +69,8 @@ def parse_arguments():
68
69
  parser.add_argument('--debug', action='store_true', help='Enable debug output')
69
70
  parser.add_argument('branchname', nargs=1, type=str,
70
71
  help='The branch name (or a partial name that matches uniquely against a local branch, remote branch, commit ID or tag)')
72
+ parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
73
+ help='Run the command in the specified directory')
71
74
 
72
75
  args = parser.parse_args()
73
76
 
@@ -76,6 +79,9 @@ def parse_arguments():
76
79
  if args.debug:
77
80
  logging.basicConfig(level=logging.INFO)
78
81
 
82
+ if args.path:
83
+ os.chdir(args.path[0])
84
+
79
85
  return args
80
86
 
81
87
  ################################################################################
@@ -198,9 +204,6 @@ def main():
198
204
  else:
199
205
  checkout_matching_branch(args, args.branchname[0])
200
206
 
201
- except git.GitError as exc:
202
- colour.error(exc.msg, exc.status)
203
-
204
207
  ################################################################################
205
208
 
206
209
  def git_co():
@@ -213,6 +216,8 @@ def git_co():
213
216
  sys.exit(1)
214
217
  except BrokenPipeError:
215
218
  sys.exit(2)
219
+ except git.GitError as exc:
220
+ colour.error(exc.msg, status=exc.status, prefix=True)
216
221
 
217
222
  ################################################################################
218
223