githubtakeout 0.1.18__tar.gz → 0.1.20__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.18 → githubtakeout-0.1.20}/LICENSE +1 -1
- {githubtakeout-0.1.18/src/githubtakeout.egg-info → githubtakeout-0.1.20}/PKG-INFO +3 -3
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/README.md +1 -1
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/pyproject.toml +7 -9
- {githubtakeout-0.1.18 → githubtakeout-0.1.20/src/githubtakeout.egg-info}/PKG-INFO +3 -3
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/githubtakeout.egg-info/requires.txt +1 -1
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/githubtakeout.py +23 -35
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/progress.py +1 -2
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/tests/test_githubtakeout.py +5 -0
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/setup.cfg +0 -0
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/githubtakeout.egg-info/SOURCES.txt +0 -0
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/githubtakeout.egg-info/dependency_links.txt +0 -0
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/githubtakeout.egg-info/entry_points.txt +0 -0
- {githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/githubtakeout.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: githubtakeout
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.20
|
|
4
4
|
Summary: Backup and archive Git Repos and Gists from GitHub
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
19
19
|
Requires-Python: >=3.12
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: GitPython==3.1.
|
|
22
|
+
Requires-Dist: GitPython==3.1.46
|
|
23
23
|
Requires-Dist: PyGithub==2.8.1
|
|
24
24
|
Requires-Dist: python-dotenv==1.2.1
|
|
25
25
|
Requires-Dist: rich==14.2.0
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
[](https://pypi.org/project/githubtakeout)
|
|
35
35
|
|
|
36
|
-
- Copyright (c) 2015-
|
|
36
|
+
- Copyright (c) 2015-2026 [Corey Goldberg][github-home]
|
|
37
37
|
- Development: [GitHub][github-repo]
|
|
38
38
|
- Download/Install: [PyPI][pypi-githubtakeout]
|
|
39
39
|
- License: [MIT][mit-license]
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://pypi.org/project/githubtakeout)
|
|
8
8
|
|
|
9
|
-
- Copyright (c) 2015-
|
|
9
|
+
- Copyright (c) 2015-2026 [Corey Goldberg][github-home]
|
|
10
10
|
- Development: [GitHub][github-repo]
|
|
11
11
|
- Download/Install: [PyPI][pypi-githubtakeout]
|
|
12
12
|
- License: [MIT][mit-license]
|
|
@@ -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.20"
|
|
8
8
|
description = "Backup and archive Git Repos and Gists from GitHub"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
@@ -23,7 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Programming Language :: Python :: 3.14",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"GitPython==3.1.
|
|
26
|
+
"GitPython==3.1.46",
|
|
27
27
|
"PyGithub==2.8.1",
|
|
28
28
|
"python-dotenv==1.2.1",
|
|
29
29
|
"rich==14.2.0",
|
|
@@ -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,3 @@ 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
|
-
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: githubtakeout
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.20
|
|
4
4
|
Summary: Backup and archive Git Repos and Gists from GitHub
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
19
19
|
Requires-Python: >=3.12
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: GitPython==3.1.
|
|
22
|
+
Requires-Dist: GitPython==3.1.46
|
|
23
23
|
Requires-Dist: PyGithub==2.8.1
|
|
24
24
|
Requires-Dist: python-dotenv==1.2.1
|
|
25
25
|
Requires-Dist: rich==14.2.0
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
[](https://pypi.org/project/githubtakeout)
|
|
35
35
|
|
|
36
|
-
- Copyright (c) 2015-
|
|
36
|
+
- Copyright (c) 2015-2026 [Corey Goldberg][github-home]
|
|
37
37
|
- Development: [GitHub][github-repo]
|
|
38
38
|
- Download/Install: [PyPI][pypi-githubtakeout]
|
|
39
39
|
- License: [MIT][mit-license]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2015-
|
|
1
|
+
# Copyright (c) 2015-2026 Corey Goldberg
|
|
2
2
|
# License: MIT
|
|
3
3
|
|
|
4
4
|
"""Backup and archive Git Repos and Gists from GitHub."""
|
|
@@ -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
|
|
|
@@ -56,14 +57,13 @@ def archive(local_repo_dir, archive_format="zip", archive_basename=None):
|
|
|
56
57
|
raise ValueError(f"{archive_format} is not a valid archive format")
|
|
57
58
|
if archive_format == "none":
|
|
58
59
|
return None
|
|
59
|
-
basename =
|
|
60
|
+
basename = local_repo_dir.name
|
|
60
61
|
extension = "tar.gz" if archive_format == "tar" else archive_format
|
|
61
62
|
if archive_basename is None:
|
|
62
63
|
archive_name = f"{basename}.{extension}"
|
|
63
64
|
else:
|
|
64
65
|
archive_name = f"{archive_basename}.{extension}"
|
|
65
|
-
|
|
66
|
-
archive_path = os.path.join(parent_dir, archive_name)
|
|
66
|
+
archive_path = local_repo_dir.parent / archive_name
|
|
67
67
|
logger.info(f"creating archive: {archive_path}")
|
|
68
68
|
if archive_format == "tar":
|
|
69
69
|
with tarfile.open(archive_path, "w:gz") as tar_archive:
|
|
@@ -72,7 +72,7 @@ def archive(local_repo_dir, archive_format="zip", archive_basename=None):
|
|
|
72
72
|
with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as zip_archive:
|
|
73
73
|
repo_path = Path(local_repo_dir)
|
|
74
74
|
for entry in repo_path.rglob("*"):
|
|
75
|
-
path =
|
|
75
|
+
path = basename / entry.relative_to(repo_path)
|
|
76
76
|
zip_archive.write(entry, arcname=path)
|
|
77
77
|
return archive_path
|
|
78
78
|
|
|
@@ -98,12 +98,10 @@ def clone(repo_url, local_repo_dir, include_history):
|
|
|
98
98
|
logger.error(e)
|
|
99
99
|
sys.exit("error: failed cloning repo")
|
|
100
100
|
finally:
|
|
101
|
-
|
|
101
|
+
# `UnboundLocalError` can occur if we catch a signal while cloning
|
|
102
|
+
with suppress(UnboundLocalError):
|
|
102
103
|
# release resources
|
|
103
104
|
repo.close()
|
|
104
|
-
except UnboundLocalError:
|
|
105
|
-
# this occurs if we catch a signal while cloning
|
|
106
|
-
pass
|
|
107
105
|
|
|
108
106
|
|
|
109
107
|
def pull(local_repo_dir):
|
|
@@ -116,12 +114,10 @@ def pull(local_repo_dir):
|
|
|
116
114
|
logger.error(e)
|
|
117
115
|
sys.exit("error: failed pulling changes in repo")
|
|
118
116
|
finally:
|
|
119
|
-
|
|
117
|
+
# `UnboundLocalError` can occur if we catch a signal while pulling
|
|
118
|
+
with suppress(UnboundLocalError):
|
|
120
119
|
# release resources
|
|
121
120
|
repo.close()
|
|
122
|
-
except UnboundLocalError:
|
|
123
|
-
# this occurs if we catch a signal while pulling
|
|
124
|
-
pass
|
|
125
121
|
|
|
126
122
|
|
|
127
123
|
def get_and_archive_repo(
|
|
@@ -146,10 +142,8 @@ def get_and_archive_repo(
|
|
|
146
142
|
needs_clone = False
|
|
147
143
|
else:
|
|
148
144
|
needs_clone = True
|
|
149
|
-
|
|
145
|
+
with suppress(FileNotFoundError):
|
|
150
146
|
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
151
|
-
except FileNotFoundError:
|
|
152
|
-
pass
|
|
153
147
|
start = default_timer()
|
|
154
148
|
if needs_clone:
|
|
155
149
|
logger.info(f"cloning repo: {repo_name} to: {local_repo_dir}")
|
|
@@ -159,11 +153,9 @@ def get_and_archive_repo(
|
|
|
159
153
|
pull(local_repo_dir)
|
|
160
154
|
if not include_history:
|
|
161
155
|
# delete the .git directory if we are not saving history
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
git_dir = Path(local_repo_dir, ".git")
|
|
157
|
+
with suppress(FileNotFoundError):
|
|
164
158
|
shutil.rmtree(git_dir, onexc=remove_readonly)
|
|
165
|
-
except FileNotFoundError:
|
|
166
|
-
pass
|
|
167
159
|
if description:
|
|
168
160
|
# clean unsafe chars and truncate description to create a useable file name
|
|
169
161
|
clean_name = re.sub(r"[/\\?%*:|\"<>\x7F\x00-\x1F]", "-", description)[:255]
|
|
@@ -174,18 +166,17 @@ def get_and_archive_repo(
|
|
|
174
166
|
local_repo_dir, archive_format, archive_basename=archive_basename
|
|
175
167
|
)
|
|
176
168
|
if archive_path:
|
|
177
|
-
size = convert_size(
|
|
169
|
+
size = convert_size(archive_path.stat().st_size)
|
|
178
170
|
logger.info(f"archive size: {size}")
|
|
179
171
|
if not keep:
|
|
172
|
+
# delete repo after archive is created
|
|
180
173
|
logger.info("deleting repo")
|
|
181
|
-
|
|
182
|
-
# delete repo after archive is created
|
|
174
|
+
with suppress(FileNotFoundError):
|
|
183
175
|
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
184
|
-
except FileNotFoundError:
|
|
185
|
-
pass
|
|
186
176
|
elapsed = default_timer() - start
|
|
187
|
-
|
|
188
|
-
|
|
177
|
+
logger.info(
|
|
178
|
+
f"successfully backed up '{local_repo_dir.name}' repo in {elapsed:.3f} secs\n"
|
|
179
|
+
)
|
|
189
180
|
|
|
190
181
|
|
|
191
182
|
def get_repos(username, token, include_gists):
|
|
@@ -255,7 +246,7 @@ def run(
|
|
|
255
246
|
list_only,
|
|
256
247
|
prompt_for_token,
|
|
257
248
|
):
|
|
258
|
-
working_dir =
|
|
249
|
+
working_dir = base_dir / "backups"
|
|
259
250
|
token = get_token(prompt_for_token)
|
|
260
251
|
|
|
261
252
|
all_repos, gists = get_repos(username, token, include_gists)
|
|
@@ -271,7 +262,7 @@ def run(
|
|
|
271
262
|
logger.info(f"creating archives in: {working_dir}\n")
|
|
272
263
|
logger.info(f"found {num_repos} repos for user '{username}':\n")
|
|
273
264
|
for repo in repos:
|
|
274
|
-
local_repo_dir =
|
|
265
|
+
local_repo_dir = working_dir / repo.name
|
|
275
266
|
url = add_creds(repo.clone_url, username, token)
|
|
276
267
|
if list_only:
|
|
277
268
|
logger.info(f"{username}/{repo.name}")
|
|
@@ -283,7 +274,7 @@ def run(
|
|
|
283
274
|
logger.info("")
|
|
284
275
|
logger.info(f"found {num_gists} gists for user '{username}':\n")
|
|
285
276
|
for gist in gists:
|
|
286
|
-
local_repo_dir =
|
|
277
|
+
local_repo_dir = working_dir / gist.id
|
|
287
278
|
url = add_creds(gist.git_pull_url, username, token)
|
|
288
279
|
if list_only:
|
|
289
280
|
logger.info(f"{username}/{gist.id}\n - {gist.description}")
|
|
@@ -299,9 +290,6 @@ def run(
|
|
|
299
290
|
|
|
300
291
|
|
|
301
292
|
def main():
|
|
302
|
-
if sys.version_info < (3, 12):
|
|
303
|
-
sys.exit("sorry, this program requires Python 3.12+")
|
|
304
|
-
|
|
305
293
|
def formatter(prog):
|
|
306
294
|
return argparse.HelpFormatter(prog, max_help_position=30)
|
|
307
295
|
|
|
@@ -309,7 +297,7 @@ def main():
|
|
|
309
297
|
parser.add_argument("username", help="github username")
|
|
310
298
|
parser.add_argument(
|
|
311
299
|
"--dir",
|
|
312
|
-
default=
|
|
300
|
+
default=Path.cwd(),
|
|
313
301
|
help="output directory (default: .)",
|
|
314
302
|
)
|
|
315
303
|
parser.add_argument(
|
|
@@ -356,7 +344,7 @@ def main():
|
|
|
356
344
|
try:
|
|
357
345
|
run(
|
|
358
346
|
username=args.username,
|
|
359
|
-
base_dir=args.dir,
|
|
347
|
+
base_dir=Path(args.dir),
|
|
360
348
|
pattern=args.pattern,
|
|
361
349
|
skip_pattern=args.skip_pattern,
|
|
362
350
|
archive_format=args.format,
|
|
File without changes
|
|
File without changes
|
{githubtakeout-0.1.18 → githubtakeout-0.1.20}/src/githubtakeout.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|