githubtakeout 0.1.0__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.0 → githubtakeout-0.1.2}/PKG-INFO +51 -29
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/README.md +49 -24
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/githubtakeout.egg-info/PKG-INFO +51 -29
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/githubtakeout.py +95 -29
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/pyproject.toml +2 -5
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/LICENSE +0 -0
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/githubtakeout.egg-info/SOURCES.txt +0 -0
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/githubtakeout.egg-info/dependency_links.txt +0 -0
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/githubtakeout.egg-info/entry_points.txt +0 -0
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/githubtakeout.egg-info/requires.txt +0 -0
- {githubtakeout-0.1.0 → githubtakeout-0.1.2}/githubtakeout.egg-info/top_level.txt +0 -0
- {githubtakeout-0.1.0 → 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
|
|
@@ -27,7 +24,7 @@ Dynamic: license-file
|
|
|
27
24
|
|
|
28
25
|
# githubtakeout
|
|
29
26
|
|
|
30
|
-
## Archive
|
|
27
|
+
## Archive Git Repos and Gists from GitHub
|
|
31
28
|
|
|
32
29
|
---
|
|
33
30
|
|
|
@@ -41,11 +38,11 @@ Dynamic: license-file
|
|
|
41
38
|
## About:
|
|
42
39
|
|
|
43
40
|
`githubtakeout` is a data export tool for archiving Git repositories hosted on GitHub.
|
|
44
|
-
It clones a user's
|
|
41
|
+
It clones a user's repos and creates an archive of each.
|
|
45
42
|
|
|
46
|
-
By default, it doesn't save
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
It supports public/private repos and public/secret gists. By default, it doesn't save
|
|
44
|
+
commit history or branches (`.git` directory), or Gist repositories (both can be enabled
|
|
45
|
+
with command line options).
|
|
49
46
|
|
|
50
47
|
When you run the program, archives of your repos will be saved in a directory named
|
|
51
48
|
`backups` inside your current working directory, unless a different location is specified
|
|
@@ -56,44 +53,38 @@ 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
|
|
63
60
|
- PyGithub
|
|
64
61
|
|
|
65
|
-
##
|
|
62
|
+
## Installation:
|
|
66
63
|
|
|
67
|
-
|
|
64
|
+
Install from [PyPI](https://pypi.org/project/githubtakeout):
|
|
68
65
|
|
|
69
66
|
```
|
|
70
67
|
pip install githubtakeout
|
|
71
68
|
```
|
|
72
69
|
|
|
73
|
-
|
|
70
|
+
## Authentication:
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
source venv/bin/activate
|
|
78
|
-
pip install githubtakeout
|
|
79
|
-
githubtakeout <github username>
|
|
80
|
-
```
|
|
72
|
+
By default, `githubtakeout` will only retrieve an account's public repos. To access
|
|
73
|
+
private repos and secret gists, you need to authenticate.
|
|
81
74
|
|
|
82
|
-
|
|
75
|
+
First, you must create a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
|
|
76
|
+
on Github (either a fine-grained or classic personal access token). Once you have a token, you can set the `GITHUB_TOKEN` environment variable:
|
|
83
77
|
|
|
84
78
|
```
|
|
85
|
-
|
|
86
|
-
cd ./githubtakeout
|
|
87
|
-
python3 -m venv venv
|
|
88
|
-
source venv/bin/activate
|
|
89
|
-
pip install .
|
|
90
|
-
githubtakeout <github username>
|
|
79
|
+
$ export GITHUB_TOKEN=<auth token created on GitHub>
|
|
91
80
|
```
|
|
92
81
|
|
|
93
|
-
|
|
82
|
+
If you prefer to be prompted for your token each time you run the program, use the `--token` argument.
|
|
83
|
+
|
|
84
|
+
### CLI Options:
|
|
94
85
|
|
|
95
86
|
```
|
|
96
|
-
usage: githubtakeout [-h] [--dir DIR] [--format FORMAT] [--gists] [--history] [--list] username
|
|
87
|
+
usage: githubtakeout [-h] [--dir DIR] [--format FORMAT] [--gists] [--history] [--list] [--token] username
|
|
97
88
|
|
|
98
89
|
positional arguments:
|
|
99
90
|
username GitHub username
|
|
@@ -105,4 +96,35 @@ options:
|
|
|
105
96
|
--gists include gists
|
|
106
97
|
--history include commit history and branches (.git directory)
|
|
107
98
|
--list list repos only
|
|
99
|
+
--token prompt for auth toke
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Usage Examples:
|
|
104
|
+
|
|
105
|
+
### Install from PyPI with pipx, Run:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pipx install githubtakeout
|
|
109
|
+
githubtakeout <github username>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Create/Activate Virtual Environment, Install from PyPI, Run:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
python3 -m venv venv
|
|
116
|
+
source venv/bin/activate
|
|
117
|
+
pip install githubtakeout
|
|
118
|
+
githubtakeout <github username>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Clone Repo, Create/Activate Virtual Environment, Install from Source, Run:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
git clone git@github.com:cgoldberg/githubtakeout.git
|
|
125
|
+
cd ./githubtakeout
|
|
126
|
+
python3 -m venv venv
|
|
127
|
+
source venv/bin/activate
|
|
128
|
+
pip install .
|
|
129
|
+
githubtakeout <github username>
|
|
108
130
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# githubtakeout
|
|
2
2
|
|
|
3
|
-
## Archive
|
|
3
|
+
## Archive Git Repos and Gists from GitHub
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
## About:
|
|
15
15
|
|
|
16
16
|
`githubtakeout` is a data export tool for archiving Git repositories hosted on GitHub.
|
|
17
|
-
It clones a user's
|
|
17
|
+
It clones a user's repos and creates an archive of each.
|
|
18
18
|
|
|
19
|
-
By default, it doesn't save
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
It supports public/private repos and public/secret gists. By default, it doesn't save
|
|
20
|
+
commit history or branches (`.git` directory), or Gist repositories (both can be enabled
|
|
21
|
+
with command line options).
|
|
22
22
|
|
|
23
23
|
When you run the program, archives of your repos will be saved in a directory named
|
|
24
24
|
`backups` inside your current working directory, unless a different location is specified
|
|
@@ -29,44 +29,38 @@ 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
|
|
36
36
|
- PyGithub
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Installation:
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Install from [PyPI](https://pypi.org/project/githubtakeout):
|
|
41
41
|
|
|
42
42
|
```
|
|
43
43
|
pip install githubtakeout
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
## Authentication:
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
source venv/bin/activate
|
|
51
|
-
pip install githubtakeout
|
|
52
|
-
githubtakeout <github username>
|
|
53
|
-
```
|
|
48
|
+
By default, `githubtakeout` will only retrieve an account's public repos. To access
|
|
49
|
+
private repos and secret gists, you need to authenticate.
|
|
54
50
|
|
|
55
|
-
|
|
51
|
+
First, you must create a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
|
|
52
|
+
on Github (either a fine-grained or classic personal access token). Once you have a token, you can set the `GITHUB_TOKEN` environment variable:
|
|
56
53
|
|
|
57
54
|
```
|
|
58
|
-
|
|
59
|
-
cd ./githubtakeout
|
|
60
|
-
python3 -m venv venv
|
|
61
|
-
source venv/bin/activate
|
|
62
|
-
pip install .
|
|
63
|
-
githubtakeout <github username>
|
|
55
|
+
$ export GITHUB_TOKEN=<auth token created on GitHub>
|
|
64
56
|
```
|
|
65
57
|
|
|
66
|
-
|
|
58
|
+
If you prefer to be prompted for your token each time you run the program, use the `--token` argument.
|
|
59
|
+
|
|
60
|
+
### CLI Options:
|
|
67
61
|
|
|
68
62
|
```
|
|
69
|
-
usage: githubtakeout [-h] [--dir DIR] [--format FORMAT] [--gists] [--history] [--list] username
|
|
63
|
+
usage: githubtakeout [-h] [--dir DIR] [--format FORMAT] [--gists] [--history] [--list] [--token] username
|
|
70
64
|
|
|
71
65
|
positional arguments:
|
|
72
66
|
username GitHub username
|
|
@@ -78,4 +72,35 @@ options:
|
|
|
78
72
|
--gists include gists
|
|
79
73
|
--history include commit history and branches (.git directory)
|
|
80
74
|
--list list repos only
|
|
75
|
+
--token prompt for auth toke
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
## Usage Examples:
|
|
80
|
+
|
|
81
|
+
### Install from PyPI with pipx, Run:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
pipx install githubtakeout
|
|
85
|
+
githubtakeout <github username>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Create/Activate Virtual Environment, Install from PyPI, Run:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
python3 -m venv venv
|
|
92
|
+
source venv/bin/activate
|
|
93
|
+
pip install githubtakeout
|
|
94
|
+
githubtakeout <github username>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Clone Repo, Create/Activate Virtual Environment, Install from Source, Run:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
git clone git@github.com:cgoldberg/githubtakeout.git
|
|
101
|
+
cd ./githubtakeout
|
|
102
|
+
python3 -m venv venv
|
|
103
|
+
source venv/bin/activate
|
|
104
|
+
pip install .
|
|
105
|
+
githubtakeout <github username>
|
|
81
106
|
```
|
|
@@ -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
|
|
@@ -27,7 +24,7 @@ Dynamic: license-file
|
|
|
27
24
|
|
|
28
25
|
# githubtakeout
|
|
29
26
|
|
|
30
|
-
## Archive
|
|
27
|
+
## Archive Git Repos and Gists from GitHub
|
|
31
28
|
|
|
32
29
|
---
|
|
33
30
|
|
|
@@ -41,11 +38,11 @@ Dynamic: license-file
|
|
|
41
38
|
## About:
|
|
42
39
|
|
|
43
40
|
`githubtakeout` is a data export tool for archiving Git repositories hosted on GitHub.
|
|
44
|
-
It clones a user's
|
|
41
|
+
It clones a user's repos and creates an archive of each.
|
|
45
42
|
|
|
46
|
-
By default, it doesn't save
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
It supports public/private repos and public/secret gists. By default, it doesn't save
|
|
44
|
+
commit history or branches (`.git` directory), or Gist repositories (both can be enabled
|
|
45
|
+
with command line options).
|
|
49
46
|
|
|
50
47
|
When you run the program, archives of your repos will be saved in a directory named
|
|
51
48
|
`backups` inside your current working directory, unless a different location is specified
|
|
@@ -56,44 +53,38 @@ 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
|
|
63
60
|
- PyGithub
|
|
64
61
|
|
|
65
|
-
##
|
|
62
|
+
## Installation:
|
|
66
63
|
|
|
67
|
-
|
|
64
|
+
Install from [PyPI](https://pypi.org/project/githubtakeout):
|
|
68
65
|
|
|
69
66
|
```
|
|
70
67
|
pip install githubtakeout
|
|
71
68
|
```
|
|
72
69
|
|
|
73
|
-
|
|
70
|
+
## Authentication:
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
source venv/bin/activate
|
|
78
|
-
pip install githubtakeout
|
|
79
|
-
githubtakeout <github username>
|
|
80
|
-
```
|
|
72
|
+
By default, `githubtakeout` will only retrieve an account's public repos. To access
|
|
73
|
+
private repos and secret gists, you need to authenticate.
|
|
81
74
|
|
|
82
|
-
|
|
75
|
+
First, you must create a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
|
|
76
|
+
on Github (either a fine-grained or classic personal access token). Once you have a token, you can set the `GITHUB_TOKEN` environment variable:
|
|
83
77
|
|
|
84
78
|
```
|
|
85
|
-
|
|
86
|
-
cd ./githubtakeout
|
|
87
|
-
python3 -m venv venv
|
|
88
|
-
source venv/bin/activate
|
|
89
|
-
pip install .
|
|
90
|
-
githubtakeout <github username>
|
|
79
|
+
$ export GITHUB_TOKEN=<auth token created on GitHub>
|
|
91
80
|
```
|
|
92
81
|
|
|
93
|
-
|
|
82
|
+
If you prefer to be prompted for your token each time you run the program, use the `--token` argument.
|
|
83
|
+
|
|
84
|
+
### CLI Options:
|
|
94
85
|
|
|
95
86
|
```
|
|
96
|
-
usage: githubtakeout [-h] [--dir DIR] [--format FORMAT] [--gists] [--history] [--list] username
|
|
87
|
+
usage: githubtakeout [-h] [--dir DIR] [--format FORMAT] [--gists] [--history] [--list] [--token] username
|
|
97
88
|
|
|
98
89
|
positional arguments:
|
|
99
90
|
username GitHub username
|
|
@@ -105,4 +96,35 @@ options:
|
|
|
105
96
|
--gists include gists
|
|
106
97
|
--history include commit history and branches (.git directory)
|
|
107
98
|
--list list repos only
|
|
99
|
+
--token prompt for auth toke
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Usage Examples:
|
|
104
|
+
|
|
105
|
+
### Install from PyPI with pipx, Run:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pipx install githubtakeout
|
|
109
|
+
githubtakeout <github username>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Create/Activate Virtual Environment, Install from PyPI, Run:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
python3 -m venv venv
|
|
116
|
+
source venv/bin/activate
|
|
117
|
+
pip install githubtakeout
|
|
118
|
+
githubtakeout <github username>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Clone Repo, Create/Activate Virtual Environment, Install from Source, Run:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
git clone git@github.com:cgoldberg/githubtakeout.git
|
|
125
|
+
cd ./githubtakeout
|
|
126
|
+
python3 -m venv venv
|
|
127
|
+
source venv/bin/activate
|
|
128
|
+
pip install .
|
|
129
|
+
githubtakeout <github username>
|
|
108
130
|
```
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
+
import getpass
|
|
4
5
|
import logging
|
|
5
6
|
import os
|
|
6
7
|
import shutil
|
|
8
|
+
import stat
|
|
9
|
+
import sys
|
|
7
10
|
import tarfile
|
|
11
|
+
import urllib
|
|
8
12
|
import zipfile
|
|
9
13
|
|
|
10
14
|
from pathlib import Path
|
|
@@ -18,6 +22,17 @@ logging.basicConfig(level=logging.INFO, format='%(message)s')
|
|
|
18
22
|
logger = logging.getLogger(__name__)
|
|
19
23
|
|
|
20
24
|
|
|
25
|
+
def add_creds(url, username, token):
|
|
26
|
+
if token is None:
|
|
27
|
+
new_url = url
|
|
28
|
+
else:
|
|
29
|
+
username = urllib.parse.quote(username)
|
|
30
|
+
url_parts = urllib.parse.urlparse(url)
|
|
31
|
+
netloc = f'{username}:{token}@{url_parts.netloc}'
|
|
32
|
+
new_url = urllib.parse.urlunparse(url_parts._replace(netloc=netloc))
|
|
33
|
+
return new_url
|
|
34
|
+
|
|
35
|
+
|
|
21
36
|
def archive(local_repo_dir, archive_format='zip', is_gist=False):
|
|
22
37
|
if archive_format not in ('tar', 'zip'):
|
|
23
38
|
raise ValueError(f'{archive_format} is not a valid archive format')
|
|
@@ -30,23 +45,28 @@ def archive(local_repo_dir, archive_format='zip', is_gist=False):
|
|
|
30
45
|
archive_path = os.path.join(parent_dir, archive_name)
|
|
31
46
|
logger.info(f'creating archive: {archive_path}')
|
|
32
47
|
if archive_format == 'tar':
|
|
33
|
-
with tarfile.open(archive_path, 'w:gz') as
|
|
34
|
-
|
|
48
|
+
with tarfile.open(archive_path, 'w:gz') as tar_archive:
|
|
49
|
+
tar_archive.add(local_repo_dir, arcname=base_name)
|
|
35
50
|
elif archive_format == 'zip':
|
|
36
|
-
with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as
|
|
51
|
+
with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as zip_archive:
|
|
37
52
|
repo_path = Path(local_repo_dir)
|
|
38
53
|
for entry in repo_path.rglob('*'):
|
|
39
54
|
path = os.path.join(base_name, entry.relative_to(repo_path))
|
|
40
|
-
|
|
55
|
+
zip_archive.write(entry, arcname=path)
|
|
41
56
|
return archive_path
|
|
42
57
|
|
|
43
58
|
|
|
44
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)
|
|
45
64
|
try:
|
|
46
|
-
shutil.rmtree(local_repo_dir)
|
|
65
|
+
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
47
66
|
except FileNotFoundError:
|
|
48
67
|
pass
|
|
49
|
-
|
|
68
|
+
repo_path = urllib.parse.urlparse(repo_url).path
|
|
69
|
+
logger.info(f'cloning repo: {repo_path} to {local_repo_dir}')
|
|
50
70
|
start = default_timer()
|
|
51
71
|
try:
|
|
52
72
|
if include_history:
|
|
@@ -55,14 +75,12 @@ def clone_and_archive_repo(repo_url, local_repo_dir, archive_format, include_his
|
|
|
55
75
|
# shallow clone (no commit history or branches)
|
|
56
76
|
git.Repo.clone_from(repo_url, local_repo_dir, multi_options=['--depth=1'])
|
|
57
77
|
try:
|
|
58
|
-
shutil.rmtree(os.path.join(local_repo_dir, '.git'))
|
|
78
|
+
shutil.rmtree(os.path.join(local_repo_dir, '.git'), onexc=remove_readonly)
|
|
59
79
|
except FileNotFoundError:
|
|
60
80
|
pass
|
|
61
81
|
except git.GitCommandError as e:
|
|
62
82
|
logger.error(e)
|
|
63
83
|
return
|
|
64
|
-
elapsed = default_timer() - start
|
|
65
|
-
logger.info(f'elapsed time: {elapsed:.3f} secs')
|
|
66
84
|
archive_path = archive(local_repo_dir, archive_format, is_gist)
|
|
67
85
|
size_kb = os.path.getsize(archive_path) / 1024
|
|
68
86
|
logger.info(f'archive size: {size_kb:.2f} KB')
|
|
@@ -71,51 +89,92 @@ def clone_and_archive_repo(repo_url, local_repo_dir, archive_format, include_his
|
|
|
71
89
|
shutil.rmtree(local_repo_dir)
|
|
72
90
|
except FileNotFoundError:
|
|
73
91
|
pass
|
|
74
|
-
|
|
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')
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_user(username, prompt_token):
|
|
98
|
+
if prompt_token:
|
|
99
|
+
token = getpass.getpass('Token:')
|
|
100
|
+
if not token:
|
|
101
|
+
sys.exit('Error: auth token cannot be empty')
|
|
102
|
+
auth = github.Auth.Token(token)
|
|
103
|
+
gh = github.Github(auth=auth)
|
|
104
|
+
else:
|
|
105
|
+
token = os.getenv('GITHUB_TOKEN')
|
|
106
|
+
if token is not None:
|
|
107
|
+
auth = github.Auth.Token(token)
|
|
108
|
+
gh = github.Github(auth=auth)
|
|
109
|
+
else:
|
|
110
|
+
gh = github.Github()
|
|
111
|
+
if token is None:
|
|
112
|
+
try:
|
|
113
|
+
user = gh.get_user(username)
|
|
114
|
+
except github.GithubException as e:
|
|
115
|
+
if e.data['status'] == '404':
|
|
116
|
+
sys.exit(f'Error: user "{username}" not found on GitHub')
|
|
117
|
+
else:
|
|
118
|
+
raise e
|
|
119
|
+
repos = user.get_repos()
|
|
120
|
+
else:
|
|
121
|
+
# you need to be authenticated and then call the API
|
|
122
|
+
# with no username to get private repos
|
|
123
|
+
user = gh.get_user()
|
|
124
|
+
repos = user.get_repos(affiliation='owner')
|
|
125
|
+
try:
|
|
126
|
+
# this just makes an API request so we can exit if unauthorized
|
|
127
|
+
_ = repos.totalCount
|
|
128
|
+
except github.GithubException as e:
|
|
129
|
+
if e.data['status'] == '401':
|
|
130
|
+
sys.exit(f'Error: invalid auth token for user "{username}" on GitHub')
|
|
131
|
+
else:
|
|
132
|
+
raise e
|
|
133
|
+
gists = user.get_gists()
|
|
134
|
+
return user, repos, gists, token
|
|
135
|
+
|
|
75
136
|
|
|
76
|
-
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):
|
|
77
138
|
working_dir = os.path.join(base_dir, 'backups')
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
except github.GithubException:
|
|
82
|
-
print(f'Error: user "{username}" not found on GitHub')
|
|
83
|
-
exit(1)
|
|
84
|
-
repos = user.get_repos()
|
|
85
|
-
num_repos = len([1 for _ in repos])
|
|
86
|
-
if not list:
|
|
139
|
+
user, repos, gists, token = get_user(username, prompt_token)
|
|
140
|
+
num_repos = repos.totalCount
|
|
141
|
+
if not list_only:
|
|
87
142
|
logger.info(f'creating archives in: {working_dir}\n')
|
|
88
143
|
logger.info(f'found {num_repos} repos for user "{username}"\n')
|
|
89
144
|
for repo in repos:
|
|
90
145
|
local_repo_dir = os.path.join(working_dir, repo.name)
|
|
91
|
-
|
|
92
|
-
|
|
146
|
+
url = add_creds(repo.clone_url, username, token)
|
|
147
|
+
if list_only:
|
|
148
|
+
logger.info(url)
|
|
93
149
|
else:
|
|
94
150
|
clone_and_archive_repo(
|
|
95
|
-
|
|
151
|
+
url,
|
|
96
152
|
local_repo_dir,
|
|
97
153
|
archive_format,
|
|
98
154
|
include_history
|
|
99
155
|
)
|
|
100
156
|
if include_gists:
|
|
101
|
-
|
|
102
|
-
num_gists = len([1 for _ in gists])
|
|
157
|
+
num_gists = gists.totalCount
|
|
103
158
|
logger.info('')
|
|
104
159
|
logger.info(f'found {num_gists} gists for user "{username}"\n')
|
|
105
160
|
for gist in gists:
|
|
106
161
|
local_repo_dir = os.path.join(working_dir, gist.id)
|
|
107
|
-
|
|
108
|
-
|
|
162
|
+
url = add_creds(gist.git_pull_url, username, token)
|
|
163
|
+
if list_only:
|
|
164
|
+
logger.info(url)
|
|
109
165
|
else:
|
|
110
166
|
clone_and_archive_repo(
|
|
111
|
-
|
|
167
|
+
url,
|
|
112
168
|
local_repo_dir,
|
|
113
169
|
archive_format,
|
|
114
170
|
include_history,
|
|
115
171
|
is_gist=True
|
|
116
172
|
)
|
|
117
173
|
|
|
174
|
+
|
|
118
175
|
def main():
|
|
176
|
+
if sys.version_info < (3, 12):
|
|
177
|
+
sys.exit('Sorry, this program requires Python 3.12+')
|
|
119
178
|
parser = argparse.ArgumentParser()
|
|
120
179
|
parser.add_argument(
|
|
121
180
|
'username',
|
|
@@ -149,6 +208,12 @@ def main():
|
|
|
149
208
|
action='store_true',
|
|
150
209
|
help='list repos only'
|
|
151
210
|
)
|
|
211
|
+
parser.add_argument(
|
|
212
|
+
'--token',
|
|
213
|
+
default=False,
|
|
214
|
+
action='store_true',
|
|
215
|
+
help='prompt for auth token'
|
|
216
|
+
)
|
|
152
217
|
args = parser.parse_args()
|
|
153
218
|
run(
|
|
154
219
|
username=args.username,
|
|
@@ -156,7 +221,8 @@ def main():
|
|
|
156
221
|
archive_format=args.format,
|
|
157
222
|
include_gists=args.gists,
|
|
158
223
|
include_history=args.history,
|
|
159
|
-
|
|
224
|
+
list_only=args.list,
|
|
225
|
+
prompt_token=args.token
|
|
160
226
|
)
|
|
161
227
|
|
|
162
228
|
|
|
@@ -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
|