github-backup 0.53.0__py3-none-any.whl → 0.55.0__py3-none-any.whl
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.
- github_backup/__init__.py +1 -1
- github_backup/github_backup.py +29 -22
- {github_backup-0.53.0.data → github_backup-0.55.0.data}/scripts/github-backup +21 -3
- {github_backup-0.53.0.dist-info → github_backup-0.55.0.dist-info}/METADATA +31 -5
- github_backup-0.55.0.dist-info/RECORD +9 -0
- github_backup-0.53.0.dist-info/RECORD +0 -9
- {github_backup-0.53.0.dist-info → github_backup-0.55.0.dist-info}/WHEEL +0 -0
- {github_backup-0.53.0.dist-info → github_backup-0.55.0.dist-info}/licenses/LICENSE.txt +0 -0
- {github_backup-0.53.0.dist-info → github_backup-0.55.0.dist-info}/top_level.txt +0 -0
- {github_backup-0.53.0.dist-info → github_backup-0.55.0.dist-info}/zip-safe +0 -0
github_backup/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.55.0"
|
github_backup/github_backup.py
CHANGED
|
@@ -561,7 +561,7 @@ def get_github_host(args):
|
|
|
561
561
|
|
|
562
562
|
|
|
563
563
|
def read_file_contents(file_uri):
|
|
564
|
-
return open(file_uri[len(FILE_URI_PREFIX)
|
|
564
|
+
return open(file_uri[len(FILE_URI_PREFIX):], "rt").readline().strip()
|
|
565
565
|
|
|
566
566
|
|
|
567
567
|
def get_github_repo_url(args, repository):
|
|
@@ -1565,16 +1565,22 @@ def retrieve_repositories(args, authenticated_user):
|
|
|
1565
1565
|
repos.extend(gists)
|
|
1566
1566
|
|
|
1567
1567
|
if args.include_starred_gists:
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1568
|
+
if not authenticated_user.get("login") or args.user.lower() != authenticated_user["login"].lower():
|
|
1569
|
+
logger.warning(
|
|
1570
|
+
"Cannot retrieve starred gists for '%s'. GitHub only allows access to the authenticated user's starred gists.",
|
|
1571
|
+
args.user,
|
|
1572
|
+
)
|
|
1573
|
+
else:
|
|
1574
|
+
starred_gists_template = "https://{0}/gists/starred".format(
|
|
1575
|
+
get_github_api_host(args)
|
|
1576
|
+
)
|
|
1577
|
+
starred_gists = retrieve_data(
|
|
1578
|
+
args, starred_gists_template, single_request=False
|
|
1579
|
+
)
|
|
1580
|
+
# flag each repo as a starred gist for downstream processing
|
|
1581
|
+
for item in starred_gists:
|
|
1582
|
+
item.update({"is_gist": True, "is_starred": True})
|
|
1583
|
+
repos.extend(starred_gists)
|
|
1578
1584
|
|
|
1579
1585
|
return repos
|
|
1580
1586
|
|
|
@@ -1666,9 +1672,10 @@ def backup_repositories(args, output_directory, repositories):
|
|
|
1666
1672
|
repo_url = get_github_repo_url(args, repository)
|
|
1667
1673
|
|
|
1668
1674
|
include_gists = args.include_gists or args.include_starred_gists
|
|
1675
|
+
include_starred = args.all_starred and repository.get("is_starred")
|
|
1669
1676
|
if (args.include_repository or args.include_everything) or (
|
|
1670
1677
|
include_gists and repository.get("is_gist")
|
|
1671
|
-
):
|
|
1678
|
+
) or include_starred:
|
|
1672
1679
|
repo_name = (
|
|
1673
1680
|
repository.get("name")
|
|
1674
1681
|
if not repository.get("is_gist")
|
|
@@ -2017,12 +2024,9 @@ def fetch_repository(
|
|
|
2017
2024
|
):
|
|
2018
2025
|
if bare_clone:
|
|
2019
2026
|
if os.path.exists(local_dir):
|
|
2020
|
-
clone_exists = (
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
)
|
|
2024
|
-
== b"true\n"
|
|
2025
|
-
)
|
|
2027
|
+
clone_exists = subprocess.check_output(
|
|
2028
|
+
["git", "rev-parse", "--is-bare-repository"], cwd=local_dir
|
|
2029
|
+
) == b"true\n"
|
|
2026
2030
|
else:
|
|
2027
2031
|
clone_exists = False
|
|
2028
2032
|
else:
|
|
@@ -2037,11 +2041,14 @@ def fetch_repository(
|
|
|
2037
2041
|
"git ls-remote " + remote_url, stdout=FNULL, stderr=FNULL, shell=True
|
|
2038
2042
|
)
|
|
2039
2043
|
if initialized == 128:
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
name
|
|
2044
|
+
if ".wiki.git" in remote_url:
|
|
2045
|
+
logger.info(
|
|
2046
|
+
"Skipping {0} wiki (wiki is enabled but has no content)".format(name)
|
|
2047
|
+
)
|
|
2048
|
+
else:
|
|
2049
|
+
logger.info(
|
|
2050
|
+
"Skipping {0} (repository not accessible - may be empty, private, or credentials invalid)".format(name)
|
|
2043
2051
|
)
|
|
2044
|
-
)
|
|
2045
2052
|
return
|
|
2046
2053
|
|
|
2047
2054
|
if clone_exists:
|
|
@@ -9,6 +9,7 @@ from github_backup.github_backup import (
|
|
|
9
9
|
backup_repositories,
|
|
10
10
|
check_git_lfs_install,
|
|
11
11
|
filter_repositories,
|
|
12
|
+
get_auth,
|
|
12
13
|
get_authenticated_user,
|
|
13
14
|
logger,
|
|
14
15
|
mkdir_p,
|
|
@@ -16,16 +17,33 @@ from github_backup.github_backup import (
|
|
|
16
17
|
retrieve_repositories,
|
|
17
18
|
)
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
# INFO and DEBUG go to stdout, WARNING and above go to stderr
|
|
21
|
+
log_format = logging.Formatter(
|
|
22
|
+
fmt="%(asctime)s.%(msecs)03d: %(message)s",
|
|
21
23
|
datefmt="%Y-%m-%dT%H:%M:%S",
|
|
22
|
-
level=logging.INFO,
|
|
23
24
|
)
|
|
24
25
|
|
|
26
|
+
stdout_handler = logging.StreamHandler(sys.stdout)
|
|
27
|
+
stdout_handler.setLevel(logging.DEBUG)
|
|
28
|
+
stdout_handler.addFilter(lambda r: r.levelno < logging.WARNING)
|
|
29
|
+
stdout_handler.setFormatter(log_format)
|
|
30
|
+
|
|
31
|
+
stderr_handler = logging.StreamHandler(sys.stderr)
|
|
32
|
+
stderr_handler.setLevel(logging.WARNING)
|
|
33
|
+
stderr_handler.setFormatter(log_format)
|
|
34
|
+
|
|
35
|
+
logging.basicConfig(level=logging.INFO, handlers=[stdout_handler, stderr_handler])
|
|
36
|
+
|
|
25
37
|
|
|
26
38
|
def main():
|
|
27
39
|
args = parse_args()
|
|
28
40
|
|
|
41
|
+
if args.private and not get_auth(args):
|
|
42
|
+
logger.warning(
|
|
43
|
+
"The --private flag has no effect without authentication. "
|
|
44
|
+
"Use -t/--token, -f/--token-fine, or -u/--username to authenticate."
|
|
45
|
+
)
|
|
46
|
+
|
|
29
47
|
if args.quiet:
|
|
30
48
|
logger.setLevel(logging.WARNING)
|
|
31
49
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-backup
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.55.0
|
|
4
4
|
Summary: backup a github user or organization
|
|
5
5
|
Home-page: http://github.com/josegonzalez/python-github-backup
|
|
6
6
|
Author: Jose Diaz-Gonzalez
|
|
@@ -311,11 +311,11 @@ If the incremental argument is used, this will result in the next backup only re
|
|
|
311
311
|
|
|
312
312
|
It's therefore recommended to only use the incremental argument if the output/result is being actively monitored, or complimented with periodic full non-incremental runs, to avoid unexpected missing data in a regular backup runs.
|
|
313
313
|
|
|
314
|
-
|
|
314
|
+
**Starred public repo hooks blocking**
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
Since the ``--all`` argument includes ``--hooks``, if you use ``--all`` and ``--all-starred`` together to clone a users starred public repositories, the backup will likely error and block the backup continuing.
|
|
317
317
|
|
|
318
|
-
|
|
318
|
+
This is due to needing the correct permission for ``--hooks`` on public repos.
|
|
319
319
|
|
|
320
320
|
|
|
321
321
|
"bare" is actually "mirror"
|
|
@@ -331,6 +331,8 @@ Starred gists vs starred repo behaviour
|
|
|
331
331
|
|
|
332
332
|
The starred normal repo cloning (``--all-starred``) argument stores starred repos separately to the users own repositories. However, using ``--starred-gists`` will store starred gists within the same directory as the users own gists ``--gists``. Also, all gist repo directory names are IDs not the gist's name.
|
|
333
333
|
|
|
334
|
+
Note: ``--starred-gists`` only retrieves starred gists for the authenticated user, not the target user, due to a GitHub API limitation.
|
|
335
|
+
|
|
334
336
|
|
|
335
337
|
Skip existing on incomplete backups
|
|
336
338
|
-----------------------------------
|
|
@@ -338,6 +340,25 @@ Skip existing on incomplete backups
|
|
|
338
340
|
The ``--skip-existing`` argument will skip a backup if the directory already exists, even if the backup in that directory failed (perhaps due to a blocking error). This may result in unexpected missing data in a regular backup.
|
|
339
341
|
|
|
340
342
|
|
|
343
|
+
Updates use fetch, not pull
|
|
344
|
+
---------------------------
|
|
345
|
+
|
|
346
|
+
When updating an existing repository backup, ``github-backup`` uses ``git fetch`` rather than ``git pull``. This is intentional - a backup tool should reliably download data without risk of failure. Using ``git pull`` would require handling merge conflicts, which adds complexity and could cause backups to fail unexpectedly.
|
|
347
|
+
|
|
348
|
+
With fetch, **all branches and commits are downloaded** safely into remote-tracking branches. The working directory files won't change, but your backup is complete.
|
|
349
|
+
|
|
350
|
+
If you look at files directly (e.g., ``cat README.md``), you'll see the old content. The new data is in the remote-tracking branches (confusingly named "remote" but stored locally). To view or use the latest files::
|
|
351
|
+
|
|
352
|
+
git show origin/main:README.md # view a file
|
|
353
|
+
git merge origin/main # update working directory
|
|
354
|
+
|
|
355
|
+
All branches are backed up as remote refs (``origin/main``, ``origin/feature-branch``, etc.).
|
|
356
|
+
|
|
357
|
+
If you want to browse files directly without merging, consider using ``--bare`` which skips the working directory entirely - the backup is just the git data.
|
|
358
|
+
|
|
359
|
+
See `#269 <https://github.com/josegonzalez/python-github-backup/issues/269>`_ for more discussion.
|
|
360
|
+
|
|
361
|
+
|
|
341
362
|
Github Backup Examples
|
|
342
363
|
======================
|
|
343
364
|
|
|
@@ -387,7 +408,12 @@ A huge thanks to all the contibuters!
|
|
|
387
408
|
Testing
|
|
388
409
|
-------
|
|
389
410
|
|
|
390
|
-
|
|
411
|
+
To run the test suite::
|
|
412
|
+
|
|
413
|
+
pip install pytest
|
|
414
|
+
pytest
|
|
415
|
+
|
|
416
|
+
To run linting::
|
|
391
417
|
|
|
392
418
|
pip install flake8
|
|
393
419
|
flake8 --ignore=E501
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
github_backup/__init__.py,sha256=UQLHFqXC6UhDcoKB4MFAazCsPAeQEfBN68BEvb4D2Tk,23
|
|
2
|
+
github_backup/github_backup.py,sha256=UMxNz_SWr7FGLYybd0RLtZu17vOwmUvZTMOlM3Uj6lk,80729
|
|
3
|
+
github_backup-0.55.0.data/scripts/github-backup,sha256=E-kXsqfjwG3h6z9wRgWOdmrCwdUBMNhJwH63sCiAPQw,2250
|
|
4
|
+
github_backup-0.55.0.dist-info/licenses/LICENSE.txt,sha256=ML-pLyn1qtAKo_DHMcQ8-p7k3f8cp-IR-mf5B49R2rk,1085
|
|
5
|
+
github_backup-0.55.0.dist-info/METADATA,sha256=xJ5GAGwpxzZhi7hBnMm4jRNtZLRKvU0lj4ClDZrd6RA,22937
|
|
6
|
+
github_backup-0.55.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
github_backup-0.55.0.dist-info/top_level.txt,sha256=Za6fKRAlxZ0b3ZBH_osPqklLe14LetdNAkvAQANT2AA,14
|
|
8
|
+
github_backup-0.55.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
+
github_backup-0.55.0.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
github_backup/__init__.py,sha256=btg9YcqDqo4fSYBUGAuFCISDFG2ovLMzO6BXZ916pVY,23
|
|
2
|
-
github_backup/github_backup.py,sha256=-7oiAGDfavMxSiqWgaiyS5vYq31kFxOXgEH0Vw5MTIo,80149
|
|
3
|
-
github_backup-0.53.0.data/scripts/github-backup,sha256=I79TNA67phytRmI9nm_LlD9h2zauXFOsOn9tYyAy9pU,1546
|
|
4
|
-
github_backup-0.53.0.dist-info/licenses/LICENSE.txt,sha256=ML-pLyn1qtAKo_DHMcQ8-p7k3f8cp-IR-mf5B49R2rk,1085
|
|
5
|
-
github_backup-0.53.0.dist-info/METADATA,sha256=fcDh7eOlfqyPFWkjuULwk8XzFoAQAa5Ld_gK_6CEc3g,21544
|
|
6
|
-
github_backup-0.53.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
github_backup-0.53.0.dist-info/top_level.txt,sha256=Za6fKRAlxZ0b3ZBH_osPqklLe14LetdNAkvAQANT2AA,14
|
|
8
|
-
github_backup-0.53.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
-
github_backup-0.53.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|