github-backup 0.64.0__py3-none-any.whl → 0.64.2__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 +82 -10
- {github_backup-0.64.0.dist-info → github_backup-0.64.2.dist-info}/METADATA +1 -1
- github_backup-0.64.2.dist-info/RECORD +12 -0
- github_backup-0.64.0.dist-info/RECORD +0 -12
- {github_backup-0.64.0.dist-info → github_backup-0.64.2.dist-info}/WHEEL +0 -0
- {github_backup-0.64.0.dist-info → github_backup-0.64.2.dist-info}/entry_points.txt +0 -0
- {github_backup-0.64.0.dist-info → github_backup-0.64.2.dist-info}/licenses/LICENSE.txt +0 -0
- {github_backup-0.64.0.dist-info → github_backup-0.64.2.dist-info}/top_level.txt +0 -0
- {github_backup-0.64.0.dist-info → github_backup-0.64.2.dist-info}/zip-safe +0 -0
github_backup/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.64.
|
|
1
|
+
__version__ = "0.64.2"
|
github_backup/github_backup.py
CHANGED
|
@@ -1789,7 +1789,39 @@ def check_git_lfs_install():
|
|
|
1789
1789
|
)
|
|
1790
1790
|
|
|
1791
1791
|
|
|
1792
|
+
# Resources fetched from their own endpoints, not from the repository listing
|
|
1793
|
+
# (gists and account-level data). Every other include_* flag needs the listing.
|
|
1794
|
+
NON_REPOSITORY_RESOURCES = frozenset(
|
|
1795
|
+
{
|
|
1796
|
+
"include_gists",
|
|
1797
|
+
"include_starred_gists",
|
|
1798
|
+
"include_starred",
|
|
1799
|
+
"include_watched",
|
|
1800
|
+
"include_followers",
|
|
1801
|
+
"include_following",
|
|
1802
|
+
}
|
|
1803
|
+
)
|
|
1804
|
+
|
|
1805
|
+
|
|
1806
|
+
def repository_list_needed(args):
|
|
1807
|
+
"""The base repository listing is only needed when a per-repository
|
|
1808
|
+
resource was requested; e.g. a gists-only backup can skip it entirely.
|
|
1809
|
+
Flags not listed in NON_REPOSITORY_RESOURCES default to needing it, so a
|
|
1810
|
+
newly added resource can never be silently skipped."""
|
|
1811
|
+
if args.repository:
|
|
1812
|
+
return True
|
|
1813
|
+
return any(
|
|
1814
|
+
value
|
|
1815
|
+
for name, value in vars(args).items()
|
|
1816
|
+
if name.startswith("include_") and name not in NON_REPOSITORY_RESOURCES
|
|
1817
|
+
)
|
|
1818
|
+
|
|
1819
|
+
|
|
1792
1820
|
def retrieve_repositories(args, authenticated_user):
|
|
1821
|
+
if not repository_list_needed(args):
|
|
1822
|
+
repos = []
|
|
1823
|
+
return retrieve_additional_repositories(args, authenticated_user, repos)
|
|
1824
|
+
|
|
1793
1825
|
logger.info("Retrieving repositories")
|
|
1794
1826
|
paginated = True
|
|
1795
1827
|
if args.user == authenticated_user["login"]:
|
|
@@ -1825,6 +1857,12 @@ def retrieve_repositories(args, authenticated_user):
|
|
|
1825
1857
|
logger.warning(f"Legal notice: {e.legal_url}")
|
|
1826
1858
|
return []
|
|
1827
1859
|
|
|
1860
|
+
return retrieve_additional_repositories(args, authenticated_user, repos)
|
|
1861
|
+
|
|
1862
|
+
|
|
1863
|
+
def retrieve_additional_repositories(args, authenticated_user, repos):
|
|
1864
|
+
"""Append starred repositories, gists and starred gists to the base
|
|
1865
|
+
repository list when requested."""
|
|
1828
1866
|
if args.all_starred:
|
|
1829
1867
|
starred_template = "https://{0}/users/{1}/starred".format(
|
|
1830
1868
|
get_github_api_host(args), args.user
|
|
@@ -2058,6 +2096,24 @@ def remove_legacy_last_update_if_migrated(
|
|
|
2058
2096
|
)
|
|
2059
2097
|
|
|
2060
2098
|
|
|
2099
|
+
def gist_backup_is_current(repository, repo_cwd, repo_dir):
|
|
2100
|
+
"""Whether fetching a gist can be skipped because a clone exists and the
|
|
2101
|
+
stored gist.json matches the listing's updated_at. Any edit to a gist —
|
|
2102
|
+
including a git push — bumps updated_at, so this can never miss a content
|
|
2103
|
+
change; at worst a comment bumps it and causes a harmless refetch."""
|
|
2104
|
+
if not os.path.exists(repo_dir):
|
|
2105
|
+
return False
|
|
2106
|
+
updated_at = repository.get("updated_at")
|
|
2107
|
+
if not updated_at:
|
|
2108
|
+
return False
|
|
2109
|
+
try:
|
|
2110
|
+
with codecs.open(os.path.join(repo_cwd, "gist.json"), encoding="utf-8") as f:
|
|
2111
|
+
stored = json.load(f)
|
|
2112
|
+
except (OSError, ValueError):
|
|
2113
|
+
return False
|
|
2114
|
+
return stored.get("updated_at") == updated_at
|
|
2115
|
+
|
|
2116
|
+
|
|
2061
2117
|
def backup_repositories(args, output_directory, repositories):
|
|
2062
2118
|
logger.info("Backing up repositories")
|
|
2063
2119
|
repos_template = "https://{0}/repos".format(get_github_api_host(args))
|
|
@@ -2065,6 +2121,7 @@ def backup_repositories(args, output_directory, repositories):
|
|
|
2065
2121
|
args, output_directory
|
|
2066
2122
|
)
|
|
2067
2123
|
incremental_resource_work_attempted = False
|
|
2124
|
+
skipped_unchanged_gists = 0
|
|
2068
2125
|
|
|
2069
2126
|
for repository in repositories:
|
|
2070
2127
|
if repository.get("is_gist"):
|
|
@@ -2098,18 +2155,30 @@ def backup_repositories(args, output_directory, repositories):
|
|
|
2098
2155
|
if not repository.get("is_gist")
|
|
2099
2156
|
else repository.get("id")
|
|
2100
2157
|
)
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2158
|
+
if repository.get("is_gist") and gist_backup_is_current(
|
|
2159
|
+
repository, repo_cwd, repo_dir
|
|
2160
|
+
):
|
|
2161
|
+
logger.debug(
|
|
2162
|
+
"Skipping gist {0} (unchanged since last backup)".format(repo_name)
|
|
2163
|
+
)
|
|
2164
|
+
skipped_unchanged_gists += 1
|
|
2165
|
+
else:
|
|
2166
|
+
fetch_repository(
|
|
2167
|
+
repo_name,
|
|
2168
|
+
repo_url,
|
|
2169
|
+
repo_dir,
|
|
2170
|
+
skip_existing=args.skip_existing,
|
|
2171
|
+
bare_clone=args.bare_clone,
|
|
2172
|
+
lfs_clone=args.lfs_clone,
|
|
2173
|
+
no_prune=args.no_prune,
|
|
2174
|
+
)
|
|
2110
2175
|
|
|
2111
2176
|
if repository.get("is_gist"):
|
|
2112
|
-
# dump gist information to a file as well
|
|
2177
|
+
# dump gist information to a file as well, even when the fetch
|
|
2178
|
+
# was skipped, so listing metadata (fork counts etc.) stays
|
|
2179
|
+
# fresh; the clone may also have been skipped entirely (e.g.
|
|
2180
|
+
# DMCA-blocked or empty gist), so make sure the directory exists
|
|
2181
|
+
mkdir_p(repo_cwd)
|
|
2113
2182
|
output_file = "{0}/gist.json".format(repo_cwd)
|
|
2114
2183
|
with codecs.open(output_file, "w", encoding="utf-8") as f:
|
|
2115
2184
|
json_dump(repository, f)
|
|
@@ -2178,6 +2247,9 @@ def backup_repositories(args, output_directory, repositories):
|
|
|
2178
2247
|
logger.info(f"Skipping remaining resources for {repository['full_name']}")
|
|
2179
2248
|
continue
|
|
2180
2249
|
|
|
2250
|
+
if skipped_unchanged_gists:
|
|
2251
|
+
logger.info("Skipped {0} unchanged gists".format(skipped_unchanged_gists))
|
|
2252
|
+
|
|
2181
2253
|
if incremental_resource_work_attempted:
|
|
2182
2254
|
remove_legacy_last_update_if_migrated(
|
|
2183
2255
|
args, output_directory, legacy_last_update_path
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
github_backup/__init__.py,sha256=HaA1w46nhUOTnwyEmiDmwgxgpts5fYS0-7T_j-jEoCs,23
|
|
2
|
+
github_backup/__main__.py,sha256=3yorCiC63maaxbjdtaeAs-G-yAAxKSO3mprhGfa1Fho,271
|
|
3
|
+
github_backup/cli.py,sha256=JTiXPOvB8id8LopREOEd1W_FLvuf9SOZU5buMRPFd9U,2922
|
|
4
|
+
github_backup/github_backup.py,sha256=N71PvIZjPedFMcuFxnirHPtnlOhx9FsMJKDJcLMirMc,119503
|
|
5
|
+
github_backup/graphql_queries.py,sha256=QBIXcGzGunilMpHfL32cTO_AAzxuQD27QII1V9K_Yw4,4883
|
|
6
|
+
github_backup-0.64.2.dist-info/licenses/LICENSE.txt,sha256=ML-pLyn1qtAKo_DHMcQ8-p7k3f8cp-IR-mf5B49R2rk,1085
|
|
7
|
+
github_backup-0.64.2.dist-info/METADATA,sha256=86x3tJ_aA1ktrSimtDh6eqgZYRVvJinaXYTLOICZETY,32426
|
|
8
|
+
github_backup-0.64.2.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
9
|
+
github_backup-0.64.2.dist-info/entry_points.txt,sha256=cLPKK7MqC77QVb6HnT2mnyL_f-ciavJG_Y9vVHlCF68,57
|
|
10
|
+
github_backup-0.64.2.dist-info/top_level.txt,sha256=Za6fKRAlxZ0b3ZBH_osPqklLe14LetdNAkvAQANT2AA,14
|
|
11
|
+
github_backup-0.64.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
12
|
+
github_backup-0.64.2.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
github_backup/__init__.py,sha256=4aEOIqCiMfp1lfhnZ0isYinRITBjjNflprA4nXib3RQ,23
|
|
2
|
-
github_backup/__main__.py,sha256=3yorCiC63maaxbjdtaeAs-G-yAAxKSO3mprhGfa1Fho,271
|
|
3
|
-
github_backup/cli.py,sha256=JTiXPOvB8id8LopREOEd1W_FLvuf9SOZU5buMRPFd9U,2922
|
|
4
|
-
github_backup/github_backup.py,sha256=S5uxqZsYMnOGBYuEX9Dy1sipyUDyaeU7qXoLjkz3nLk,116637
|
|
5
|
-
github_backup/graphql_queries.py,sha256=QBIXcGzGunilMpHfL32cTO_AAzxuQD27QII1V9K_Yw4,4883
|
|
6
|
-
github_backup-0.64.0.dist-info/licenses/LICENSE.txt,sha256=ML-pLyn1qtAKo_DHMcQ8-p7k3f8cp-IR-mf5B49R2rk,1085
|
|
7
|
-
github_backup-0.64.0.dist-info/METADATA,sha256=XSm6FyyldS86S0nFxbOMcS1lNkP0vVb8nTNLAMdF0J4,32426
|
|
8
|
-
github_backup-0.64.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
9
|
-
github_backup-0.64.0.dist-info/entry_points.txt,sha256=cLPKK7MqC77QVb6HnT2mnyL_f-ciavJG_Y9vVHlCF68,57
|
|
10
|
-
github_backup-0.64.0.dist-info/top_level.txt,sha256=Za6fKRAlxZ0b3ZBH_osPqklLe14LetdNAkvAQANT2AA,14
|
|
11
|
-
github_backup-0.64.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
12
|
-
github_backup-0.64.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|