githubtakeout 0.1.1__tar.gz → 0.1.2__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.1 → githubtakeout-0.1.2}/PKG-INFO +10 -6
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/README.md +8 -1
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/githubtakeout.egg-info/PKG-INFO +10 -6
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/githubtakeout.py +26 -21
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/pyproject.toml +2 -5
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/LICENSE +0 -0
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/githubtakeout.egg-info/SOURCES.txt +0 -0
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/githubtakeout.egg-info/dependency_links.txt +0 -0
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/githubtakeout.egg-info/entry_points.txt +0 -0
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/githubtakeout.egg-info/requires.txt +0 -0
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/githubtakeout.egg-info/top_level.txt +0 -0
- {githubtakeout-0.1.1 → githubtakeout-0.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: githubtakeout
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Archive public Git Repos and Gists from GitHub
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -13,12 +13,9 @@ Classifier: Environment :: Console
|
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Programming Language :: Python
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.12
|
|
22
19
|
Description-Content-Type: text/markdown
|
|
23
20
|
License-File: LICENSE
|
|
24
21
|
Requires-Dist: GitPython==3.1.44
|
|
@@ -56,7 +53,7 @@ as tarballs (`.tar.gz`) using the `--format=tar` option.
|
|
|
56
53
|
|
|
57
54
|
## Requirements:
|
|
58
55
|
|
|
59
|
-
- Python 3.
|
|
56
|
+
- Python 3.12+
|
|
60
57
|
- Git 1.7+
|
|
61
58
|
- Python packages:
|
|
62
59
|
- GitPython
|
|
@@ -105,6 +102,13 @@ options:
|
|
|
105
102
|
|
|
106
103
|
## Usage Examples:
|
|
107
104
|
|
|
105
|
+
### Install from PyPI with pipx, Run:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pipx install githubtakeout
|
|
109
|
+
githubtakeout <github username>
|
|
110
|
+
```
|
|
111
|
+
|
|
108
112
|
### Create/Activate Virtual Environment, Install from PyPI, Run:
|
|
109
113
|
|
|
110
114
|
```
|
|
@@ -29,7 +29,7 @@ as tarballs (`.tar.gz`) using the `--format=tar` option.
|
|
|
29
29
|
|
|
30
30
|
## Requirements:
|
|
31
31
|
|
|
32
|
-
- Python 3.
|
|
32
|
+
- Python 3.12+
|
|
33
33
|
- Git 1.7+
|
|
34
34
|
- Python packages:
|
|
35
35
|
- GitPython
|
|
@@ -78,6 +78,13 @@ options:
|
|
|
78
78
|
|
|
79
79
|
## Usage Examples:
|
|
80
80
|
|
|
81
|
+
### Install from PyPI with pipx, Run:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
pipx install githubtakeout
|
|
85
|
+
githubtakeout <github username>
|
|
86
|
+
```
|
|
87
|
+
|
|
81
88
|
### Create/Activate Virtual Environment, Install from PyPI, Run:
|
|
82
89
|
|
|
83
90
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: githubtakeout
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Archive public Git Repos and Gists from GitHub
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -13,12 +13,9 @@ Classifier: Environment :: Console
|
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Programming Language :: Python
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.12
|
|
22
19
|
Description-Content-Type: text/markdown
|
|
23
20
|
License-File: LICENSE
|
|
24
21
|
Requires-Dist: GitPython==3.1.44
|
|
@@ -56,7 +53,7 @@ as tarballs (`.tar.gz`) using the `--format=tar` option.
|
|
|
56
53
|
|
|
57
54
|
## Requirements:
|
|
58
55
|
|
|
59
|
-
- Python 3.
|
|
56
|
+
- Python 3.12+
|
|
60
57
|
- Git 1.7+
|
|
61
58
|
- Python packages:
|
|
62
59
|
- GitPython
|
|
@@ -105,6 +102,13 @@ options:
|
|
|
105
102
|
|
|
106
103
|
## Usage Examples:
|
|
107
104
|
|
|
105
|
+
### Install from PyPI with pipx, Run:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pipx install githubtakeout
|
|
109
|
+
githubtakeout <github username>
|
|
110
|
+
```
|
|
111
|
+
|
|
108
112
|
### Create/Activate Virtual Environment, Install from PyPI, Run:
|
|
109
113
|
|
|
110
114
|
```
|
|
@@ -5,6 +5,8 @@ import getpass
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
7
|
import shutil
|
|
8
|
+
import stat
|
|
9
|
+
import sys
|
|
8
10
|
import tarfile
|
|
9
11
|
import urllib
|
|
10
12
|
import zipfile
|
|
@@ -43,20 +45,24 @@ def archive(local_repo_dir, archive_format='zip', is_gist=False):
|
|
|
43
45
|
archive_path = os.path.join(parent_dir, archive_name)
|
|
44
46
|
logger.info(f'creating archive: {archive_path}')
|
|
45
47
|
if archive_format == 'tar':
|
|
46
|
-
with tarfile.open(archive_path, 'w:gz') as
|
|
47
|
-
|
|
48
|
+
with tarfile.open(archive_path, 'w:gz') as tar_archive:
|
|
49
|
+
tar_archive.add(local_repo_dir, arcname=base_name)
|
|
48
50
|
elif archive_format == 'zip':
|
|
49
|
-
with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as
|
|
51
|
+
with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as zip_archive:
|
|
50
52
|
repo_path = Path(local_repo_dir)
|
|
51
53
|
for entry in repo_path.rglob('*'):
|
|
52
54
|
path = os.path.join(base_name, entry.relative_to(repo_path))
|
|
53
|
-
|
|
55
|
+
zip_archive.write(entry, arcname=path)
|
|
54
56
|
return archive_path
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
def clone_and_archive_repo(repo_url, local_repo_dir, archive_format, include_history, is_gist=False):
|
|
60
|
+
def remove_readonly(func, path, _):
|
|
61
|
+
# clear the readonly bit and reattempt removal
|
|
62
|
+
os.chmod(path, stat.S_IWRITE)
|
|
63
|
+
func(path)
|
|
58
64
|
try:
|
|
59
|
-
shutil.rmtree(local_repo_dir)
|
|
65
|
+
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
60
66
|
except FileNotFoundError:
|
|
61
67
|
pass
|
|
62
68
|
repo_path = urllib.parse.urlparse(repo_url).path
|
|
@@ -69,14 +75,12 @@ def clone_and_archive_repo(repo_url, local_repo_dir, archive_format, include_his
|
|
|
69
75
|
# shallow clone (no commit history or branches)
|
|
70
76
|
git.Repo.clone_from(repo_url, local_repo_dir, multi_options=['--depth=1'])
|
|
71
77
|
try:
|
|
72
|
-
shutil.rmtree(os.path.join(local_repo_dir, '.git'))
|
|
78
|
+
shutil.rmtree(os.path.join(local_repo_dir, '.git'), onexc=remove_readonly)
|
|
73
79
|
except FileNotFoundError:
|
|
74
80
|
pass
|
|
75
81
|
except git.GitCommandError as e:
|
|
76
82
|
logger.error(e)
|
|
77
83
|
return
|
|
78
|
-
elapsed = default_timer() - start
|
|
79
|
-
logger.info(f'elapsed time: {elapsed:.3f} secs')
|
|
80
84
|
archive_path = archive(local_repo_dir, archive_format, is_gist)
|
|
81
85
|
size_kb = os.path.getsize(archive_path) / 1024
|
|
82
86
|
logger.info(f'archive size: {size_kb:.2f} KB')
|
|
@@ -85,15 +89,16 @@ def clone_and_archive_repo(repo_url, local_repo_dir, archive_format, include_his
|
|
|
85
89
|
shutil.rmtree(local_repo_dir)
|
|
86
90
|
except FileNotFoundError:
|
|
87
91
|
pass
|
|
88
|
-
|
|
92
|
+
elapsed = default_timer() - start
|
|
93
|
+
base_name = os.path.basename(local_repo_dir)
|
|
94
|
+
logger.info(f'successfully backed up "{base_name}" repo in {elapsed:.3f} secs\n')
|
|
89
95
|
|
|
90
96
|
|
|
91
97
|
def get_user(username, prompt_token):
|
|
92
98
|
if prompt_token:
|
|
93
99
|
token = getpass.getpass('Token:')
|
|
94
100
|
if not token:
|
|
95
|
-
|
|
96
|
-
exit(1)
|
|
101
|
+
sys.exit('Error: auth token cannot be empty')
|
|
97
102
|
auth = github.Auth.Token(token)
|
|
98
103
|
gh = github.Github(auth=auth)
|
|
99
104
|
else:
|
|
@@ -108,10 +113,9 @@ def get_user(username, prompt_token):
|
|
|
108
113
|
user = gh.get_user(username)
|
|
109
114
|
except github.GithubException as e:
|
|
110
115
|
if e.data['status'] == '404':
|
|
111
|
-
|
|
116
|
+
sys.exit(f'Error: user "{username}" not found on GitHub')
|
|
112
117
|
else:
|
|
113
118
|
raise e
|
|
114
|
-
exit(1)
|
|
115
119
|
repos = user.get_repos()
|
|
116
120
|
else:
|
|
117
121
|
# you need to be authenticated and then call the API
|
|
@@ -120,28 +124,27 @@ def get_user(username, prompt_token):
|
|
|
120
124
|
repos = user.get_repos(affiliation='owner')
|
|
121
125
|
try:
|
|
122
126
|
# this just makes an API request so we can exit if unauthorized
|
|
123
|
-
repos.totalCount
|
|
127
|
+
_ = repos.totalCount
|
|
124
128
|
except github.GithubException as e:
|
|
125
129
|
if e.data['status'] == '401':
|
|
126
|
-
|
|
130
|
+
sys.exit(f'Error: invalid auth token for user "{username}" on GitHub')
|
|
127
131
|
else:
|
|
128
132
|
raise e
|
|
129
|
-
exit(1)
|
|
130
133
|
gists = user.get_gists()
|
|
131
134
|
return user, repos, gists, token
|
|
132
135
|
|
|
133
136
|
|
|
134
|
-
def run(username, base_dir, archive_format, include_gists, include_history,
|
|
137
|
+
def run(username, base_dir, archive_format, include_gists, include_history, list_only, prompt_token):
|
|
135
138
|
working_dir = os.path.join(base_dir, 'backups')
|
|
136
139
|
user, repos, gists, token = get_user(username, prompt_token)
|
|
137
140
|
num_repos = repos.totalCount
|
|
138
|
-
if not
|
|
141
|
+
if not list_only:
|
|
139
142
|
logger.info(f'creating archives in: {working_dir}\n')
|
|
140
143
|
logger.info(f'found {num_repos} repos for user "{username}"\n')
|
|
141
144
|
for repo in repos:
|
|
142
145
|
local_repo_dir = os.path.join(working_dir, repo.name)
|
|
143
146
|
url = add_creds(repo.clone_url, username, token)
|
|
144
|
-
if
|
|
147
|
+
if list_only:
|
|
145
148
|
logger.info(url)
|
|
146
149
|
else:
|
|
147
150
|
clone_and_archive_repo(
|
|
@@ -157,7 +160,7 @@ def run(username, base_dir, archive_format, include_gists, include_history, list
|
|
|
157
160
|
for gist in gists:
|
|
158
161
|
local_repo_dir = os.path.join(working_dir, gist.id)
|
|
159
162
|
url = add_creds(gist.git_pull_url, username, token)
|
|
160
|
-
if
|
|
163
|
+
if list_only:
|
|
161
164
|
logger.info(url)
|
|
162
165
|
else:
|
|
163
166
|
clone_and_archive_repo(
|
|
@@ -170,6 +173,8 @@ def run(username, base_dir, archive_format, include_gists, include_history, list
|
|
|
170
173
|
|
|
171
174
|
|
|
172
175
|
def main():
|
|
176
|
+
if sys.version_info < (3, 12):
|
|
177
|
+
sys.exit('Sorry, this program requires Python 3.12+')
|
|
173
178
|
parser = argparse.ArgumentParser()
|
|
174
179
|
parser.add_argument(
|
|
175
180
|
'username',
|
|
@@ -216,7 +221,7 @@ def main():
|
|
|
216
221
|
archive_format=args.format,
|
|
217
222
|
include_gists=args.gists,
|
|
218
223
|
include_history=args.history,
|
|
219
|
-
|
|
224
|
+
list_only=args.list,
|
|
220
225
|
prompt_token=args.token
|
|
221
226
|
)
|
|
222
227
|
|
|
@@ -4,23 +4,20 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "githubtakeout"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Archive public Git Repos and Gists from GitHub"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
11
11
|
authors = [{name = "Corey Goldberg"}]
|
|
12
12
|
maintainers = [{name = "Corey Goldberg"}]
|
|
13
13
|
readme = "README.md"
|
|
14
|
-
requires-python = ">=3.
|
|
14
|
+
requires-python = ">= 3.12"
|
|
15
15
|
keywords = ["git", "github", "archive", "backup", "export", "takeout"]
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Environment :: Console",
|
|
18
18
|
"Intended Audience :: Developers",
|
|
19
19
|
"Programming Language :: Python",
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
21
|
-
"Programming Language :: Python :: 3.9",
|
|
22
|
-
"Programming Language :: Python :: 3.10",
|
|
23
|
-
"Programming Language :: Python :: 3.11",
|
|
24
21
|
"Programming Language :: Python :: 3.12",
|
|
25
22
|
"Programming Language :: Python :: 3.13"
|
|
26
23
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|