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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: githubtakeout
3
- Version: 0.1.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.9
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.9+
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.9+
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.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.9
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.9+
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 tar:
47
- tar.add(local_repo_dir, arcname=base_name)
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 zip:
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
- zip.write(entry, arcname=path)
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
- logger.info('')
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
- print(f'Error: auth token cannot be empty')
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
- print(f'Error: user "{username}" not found on GitHub')
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
- print(f'Error: invalid auth token for user "{username}" on GitHub')
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, list, prompt_token):
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 list:
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 list:
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 list:
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
- list=args.list,
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.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.9"
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