githubtakeout 0.1.17__tar.gz → 0.1.19__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.
- {githubtakeout-0.1.17/src/githubtakeout.egg-info → githubtakeout-0.1.19}/PKG-INFO +1 -1
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/pyproject.toml +7 -8
- {githubtakeout-0.1.17 → githubtakeout-0.1.19/src/githubtakeout.egg-info}/PKG-INFO +1 -1
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/githubtakeout.py +39 -42
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/LICENSE +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/README.md +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/setup.cfg +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/githubtakeout.egg-info/SOURCES.txt +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/githubtakeout.egg-info/dependency_links.txt +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/githubtakeout.egg-info/entry_points.txt +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/githubtakeout.egg-info/requires.txt +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/githubtakeout.egg-info/top_level.txt +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/progress.py +0 -0
- {githubtakeout-0.1.17 → githubtakeout-0.1.19}/tests/test_githubtakeout.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "githubtakeout"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.19"
|
|
8
8
|
description = "Backup and archive Git Repos and Gists from GitHub"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
@@ -73,14 +73,15 @@ respect-gitignore = true
|
|
|
73
73
|
target-version = "py312"
|
|
74
74
|
|
|
75
75
|
[tool.ruff.lint]
|
|
76
|
+
fixable = ["ALL"]
|
|
76
77
|
select = [
|
|
77
|
-
"D", "E", "F", "I", "
|
|
78
|
+
"D", "E", "F", "I", "N", "FURB", "PT",
|
|
79
|
+
"RUF", "TID", "UP", "W"
|
|
78
80
|
]
|
|
79
81
|
extend-ignore = [
|
|
80
|
-
"D100", "D101", "D102", "D103", "
|
|
81
|
-
"
|
|
82
|
+
"D100", "D101", "D102", "D103", "D105",
|
|
83
|
+
"D107", "D203", "D213", "RUF012"
|
|
82
84
|
]
|
|
83
|
-
fixable = ["ALL"]
|
|
84
85
|
|
|
85
86
|
[tool.ruff.format]
|
|
86
87
|
docstring-code-format = true
|
|
@@ -89,6 +90,4 @@ docstring-code-line-length = 88
|
|
|
89
90
|
[tool.refurb]
|
|
90
91
|
python_version = "3.12"
|
|
91
92
|
enable_all = true
|
|
92
|
-
disable = [
|
|
93
|
-
"FURB104", "FURB107", "FURB147", "FURB173", "FURB183", "FURB184"
|
|
94
|
-
]
|
|
93
|
+
disable = []
|
|
@@ -15,6 +15,7 @@ import sys
|
|
|
15
15
|
import tarfile
|
|
16
16
|
import urllib
|
|
17
17
|
import zipfile
|
|
18
|
+
from contextlib import suppress
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
from timeit import default_timer
|
|
20
21
|
|
|
@@ -51,27 +52,27 @@ def add_creds(url, username, token):
|
|
|
51
52
|
return new_url
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
def archive(local_repo_dir, archive_format="zip",
|
|
55
|
+
def archive(local_repo_dir, archive_format="zip", archive_basename=None):
|
|
55
56
|
if archive_format not in ARCHIVE_FORMATS:
|
|
56
57
|
raise ValueError(f"{archive_format} is not a valid archive format")
|
|
57
58
|
if archive_format == "none":
|
|
58
59
|
return None
|
|
59
|
-
|
|
60
|
+
basename = local_repo_dir.stem
|
|
60
61
|
extension = "tar.gz" if archive_format == "tar" else archive_format
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
archive_path =
|
|
62
|
+
if archive_basename is None:
|
|
63
|
+
archive_name = f"{basename}.{extension}"
|
|
64
|
+
else:
|
|
65
|
+
archive_name = f"{archive_basename}.{extension}"
|
|
66
|
+
archive_path = local_repo_dir.parent / archive_name
|
|
66
67
|
logger.info(f"creating archive: {archive_path}")
|
|
67
68
|
if archive_format == "tar":
|
|
68
69
|
with tarfile.open(archive_path, "w:gz") as tar_archive:
|
|
69
|
-
tar_archive.add(local_repo_dir, arcname=
|
|
70
|
+
tar_archive.add(local_repo_dir, arcname=basename)
|
|
70
71
|
elif archive_format == "zip":
|
|
71
72
|
with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as zip_archive:
|
|
72
73
|
repo_path = Path(local_repo_dir)
|
|
73
74
|
for entry in repo_path.rglob("*"):
|
|
74
|
-
path =
|
|
75
|
+
path = basename / entry.relative_to(repo_path)
|
|
75
76
|
zip_archive.write(entry, arcname=path)
|
|
76
77
|
return archive_path
|
|
77
78
|
|
|
@@ -97,12 +98,10 @@ def clone(repo_url, local_repo_dir, include_history):
|
|
|
97
98
|
logger.error(e)
|
|
98
99
|
sys.exit("error: failed cloning repo")
|
|
99
100
|
finally:
|
|
100
|
-
|
|
101
|
+
# `UnboundLocalError` can occur if we catch a signal while cloning
|
|
102
|
+
with suppress(UnboundLocalError):
|
|
101
103
|
# release resources
|
|
102
104
|
repo.close()
|
|
103
|
-
except UnboundLocalError:
|
|
104
|
-
# this occurs if we catch a signal while cloning
|
|
105
|
-
pass
|
|
106
105
|
|
|
107
106
|
|
|
108
107
|
def pull(local_repo_dir):
|
|
@@ -115,16 +114,14 @@ def pull(local_repo_dir):
|
|
|
115
114
|
logger.error(e)
|
|
116
115
|
sys.exit("error: failed pulling changes in repo")
|
|
117
116
|
finally:
|
|
118
|
-
|
|
117
|
+
# `UnboundLocalError` can occur if we catch a signal while pulling
|
|
118
|
+
with suppress(UnboundLocalError):
|
|
119
119
|
# release resources
|
|
120
120
|
repo.close()
|
|
121
|
-
except UnboundLocalError:
|
|
122
|
-
# this occurs if we catch a signal while pulling
|
|
123
|
-
pass
|
|
124
121
|
|
|
125
122
|
|
|
126
123
|
def get_and_archive_repo(
|
|
127
|
-
repo_url, local_repo_dir, archive_format, include_history, keep,
|
|
124
|
+
repo_url, local_repo_dir, archive_format, include_history, keep, description=None
|
|
128
125
|
):
|
|
129
126
|
def remove_readonly(func, path, _):
|
|
130
127
|
# This is necessary so rmtree() doesn't fail if there are any readonly
|
|
@@ -145,10 +142,8 @@ def get_and_archive_repo(
|
|
|
145
142
|
needs_clone = False
|
|
146
143
|
else:
|
|
147
144
|
needs_clone = True
|
|
148
|
-
|
|
145
|
+
with suppress(FileNotFoundError):
|
|
149
146
|
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
150
|
-
except FileNotFoundError:
|
|
151
|
-
pass
|
|
152
147
|
start = default_timer()
|
|
153
148
|
if needs_clone:
|
|
154
149
|
logger.info(f"cloning repo: {repo_name} to: {local_repo_dir}")
|
|
@@ -158,25 +153,30 @@ def get_and_archive_repo(
|
|
|
158
153
|
pull(local_repo_dir)
|
|
159
154
|
if not include_history:
|
|
160
155
|
# delete the .git directory if we are not saving history
|
|
161
|
-
|
|
162
|
-
|
|
156
|
+
git_dir = Path(local_repo_dir, ".git")
|
|
157
|
+
with suppress(FileNotFoundError):
|
|
163
158
|
shutil.rmtree(git_dir, onexc=remove_readonly)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
159
|
+
if description:
|
|
160
|
+
# clean unsafe chars and truncate description to create a useable file name
|
|
161
|
+
clean_name = re.sub(r"[/\\?%*:|\"<>\x7F\x00-\x1F]", "-", description)[:255]
|
|
162
|
+
archive_basename = f"gist - {clean_name}"
|
|
163
|
+
else:
|
|
164
|
+
archive_basename = None
|
|
165
|
+
archive_path = archive(
|
|
166
|
+
local_repo_dir, archive_format, archive_basename=archive_basename
|
|
167
|
+
)
|
|
167
168
|
if archive_path:
|
|
168
|
-
size = convert_size(
|
|
169
|
+
size = convert_size(archive_path.stat().st_size)
|
|
169
170
|
logger.info(f"archive size: {size}")
|
|
170
171
|
if not keep:
|
|
172
|
+
# delete repo after archive is created
|
|
171
173
|
logger.info("deleting repo")
|
|
172
|
-
|
|
173
|
-
# delete repo after archive is created
|
|
174
|
+
with suppress(FileNotFoundError):
|
|
174
175
|
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
175
|
-
except FileNotFoundError:
|
|
176
|
-
pass
|
|
177
176
|
elapsed = default_timer() - start
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
logger.info(
|
|
178
|
+
f"successfully backed up '{local_repo_dir.stem}' repo in {elapsed:.3f} secs\n"
|
|
179
|
+
)
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
def get_repos(username, token, include_gists):
|
|
@@ -246,7 +246,7 @@ def run(
|
|
|
246
246
|
list_only,
|
|
247
247
|
prompt_for_token,
|
|
248
248
|
):
|
|
249
|
-
working_dir =
|
|
249
|
+
working_dir = base_dir / "backups"
|
|
250
250
|
token = get_token(prompt_for_token)
|
|
251
251
|
|
|
252
252
|
all_repos, gists = get_repos(username, token, include_gists)
|
|
@@ -262,7 +262,7 @@ def run(
|
|
|
262
262
|
logger.info(f"creating archives in: {working_dir}\n")
|
|
263
263
|
logger.info(f"found {num_repos} repos for user '{username}':\n")
|
|
264
264
|
for repo in repos:
|
|
265
|
-
local_repo_dir =
|
|
265
|
+
local_repo_dir = working_dir / repo.name
|
|
266
266
|
url = add_creds(repo.clone_url, username, token)
|
|
267
267
|
if list_only:
|
|
268
268
|
logger.info(f"{username}/{repo.name}")
|
|
@@ -274,7 +274,7 @@ def run(
|
|
|
274
274
|
logger.info("")
|
|
275
275
|
logger.info(f"found {num_gists} gists for user '{username}':\n")
|
|
276
276
|
for gist in gists:
|
|
277
|
-
local_repo_dir =
|
|
277
|
+
local_repo_dir = working_dir / gist.id
|
|
278
278
|
url = add_creds(gist.git_pull_url, username, token)
|
|
279
279
|
if list_only:
|
|
280
280
|
logger.info(f"{username}/{gist.id}\n - {gist.description}")
|
|
@@ -285,14 +285,11 @@ def run(
|
|
|
285
285
|
archive_format,
|
|
286
286
|
include_history,
|
|
287
287
|
keep,
|
|
288
|
-
|
|
288
|
+
description=gist.description,
|
|
289
289
|
)
|
|
290
290
|
|
|
291
291
|
|
|
292
292
|
def main():
|
|
293
|
-
if sys.version_info < (3, 12):
|
|
294
|
-
sys.exit("sorry, this program requires Python 3.12+")
|
|
295
|
-
|
|
296
293
|
def formatter(prog):
|
|
297
294
|
return argparse.HelpFormatter(prog, max_help_position=30)
|
|
298
295
|
|
|
@@ -300,7 +297,7 @@ def main():
|
|
|
300
297
|
parser.add_argument("username", help="github username")
|
|
301
298
|
parser.add_argument(
|
|
302
299
|
"--dir",
|
|
303
|
-
default=
|
|
300
|
+
default=Path.cwd(),
|
|
304
301
|
help="output directory (default: .)",
|
|
305
302
|
)
|
|
306
303
|
parser.add_argument(
|
|
@@ -347,7 +344,7 @@ def main():
|
|
|
347
344
|
try:
|
|
348
345
|
run(
|
|
349
346
|
username=args.username,
|
|
350
|
-
base_dir=args.dir,
|
|
347
|
+
base_dir=Path(args.dir),
|
|
351
348
|
pattern=args.pattern,
|
|
352
349
|
skip_pattern=args.skip_pattern,
|
|
353
350
|
archive_format=args.format,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{githubtakeout-0.1.17 → githubtakeout-0.1.19}/src/githubtakeout.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|