github-backup 0.58.0__py3-none-any.whl → 0.60.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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.58.0"
1
+ __version__ = "0.60.0"
github_backup/cli.py CHANGED
@@ -43,7 +43,7 @@ def main():
43
43
  if args.private and not get_auth(args):
44
44
  logger.warning(
45
45
  "The --private flag has no effect without authentication. "
46
- "Use -t/--token, -f/--token-fine, or -u/--username to authenticate."
46
+ "Use -t/--token or -f/--token-fine to authenticate."
47
47
  )
48
48
 
49
49
  if args.quiet:
@@ -7,7 +7,6 @@ import base64
7
7
  import calendar
8
8
  import codecs
9
9
  import errno
10
- import getpass
11
10
  import json
12
11
  import logging
13
12
  import os
@@ -24,7 +23,6 @@ from collections.abc import Generator
24
23
  from datetime import datetime
25
24
  from http.client import IncompleteRead
26
25
  from urllib.error import HTTPError, URLError
27
- from urllib.parse import quote as urlquote
28
26
  from urllib.parse import urlencode, urlparse
29
27
  from urllib.request import HTTPRedirectHandler, Request, build_opener, urlopen
30
28
 
@@ -76,9 +74,6 @@ else:
76
74
  " 3. Debian/Ubuntu: apt-get install ca-certificates\n\n"
77
75
  )
78
76
 
79
- # Retry configuration
80
- MAX_RETRIES = 5
81
-
82
77
 
83
78
  def logging_subprocess(
84
79
  popenargs, stdout_log_level=logging.DEBUG, stderr_log_level=logging.ERROR, **kwargs
@@ -146,20 +141,20 @@ def mask_password(url, secret="*****"):
146
141
  return url.replace(parsed.password, secret)
147
142
 
148
143
 
144
+ def non_negative_int(value):
145
+ """Argparse type validator for non-negative integers."""
146
+ try:
147
+ ivalue = int(value)
148
+ except ValueError:
149
+ raise argparse.ArgumentTypeError(f"'{value}' is not a valid integer")
150
+ if ivalue < 0:
151
+ raise argparse.ArgumentTypeError(f"{value} must be 0 or greater")
152
+ return ivalue
153
+
154
+
149
155
  def parse_args(args=None):
150
156
  parser = argparse.ArgumentParser(description="Backup a github account")
151
157
  parser.add_argument("user", metavar="USER", type=str, help="github username")
152
- parser.add_argument(
153
- "-u", "--username", dest="username", help="username for basic auth"
154
- )
155
- parser.add_argument(
156
- "-p",
157
- "--password",
158
- dest="password",
159
- help="password for basic auth. "
160
- "If a username is given but not a password, the "
161
- "password will be prompted for.",
162
- )
163
158
  parser.add_argument(
164
159
  "-t",
165
160
  "--token",
@@ -224,6 +219,13 @@ def parse_args(args=None):
224
219
  dest="all_starred",
225
220
  help="include starred repositories in backup [*]",
226
221
  )
222
+ parser.add_argument(
223
+ "--starred-skip-size-over",
224
+ type=int,
225
+ metavar="MB",
226
+ dest="starred_skip_size_over",
227
+ help="skip starred repositories larger than this size in MB",
228
+ )
227
229
  parser.add_argument(
228
230
  "--watched",
229
231
  action="store_true",
@@ -474,6 +476,13 @@ def parse_args(args=None):
474
476
  parser.add_argument(
475
477
  "--exclude", dest="exclude", help="names of repositories to exclude", nargs="*"
476
478
  )
479
+ parser.add_argument(
480
+ "--retries",
481
+ dest="max_retries",
482
+ type=non_negative_int,
483
+ default=5,
484
+ help="maximum number of retries for API calls (default: 5)",
485
+ )
477
486
  return parser.parse_args(args)
478
487
 
479
488
 
@@ -533,16 +542,6 @@ def get_auth(args, encode=True, for_git_cli=False):
533
542
  auth = args.token_classic
534
543
  else:
535
544
  auth = "x-access-token:" + args.token_classic
536
- elif args.username:
537
- if not args.password:
538
- args.password = getpass.getpass()
539
- if encode:
540
- password = args.password
541
- else:
542
- password = urlquote(args.password)
543
- auth = args.username + ":" + password
544
- elif args.password:
545
- raise Exception("You must specify a username for basic auth")
546
545
 
547
546
  if not auth:
548
547
  return None
@@ -638,7 +637,7 @@ def retrieve_data(args, template, query_args=None, paginated=True):
638
637
  def _extract_next_page_url(link_header):
639
638
  for link in link_header.split(","):
640
639
  if 'rel="next"' in link:
641
- return link[link.find("<") + 1:link.find(">")]
640
+ return link[link.find("<") + 1 : link.find(">")]
642
641
  return None
643
642
 
644
643
  def fetch_all() -> Generator[dict, None, None]:
@@ -647,7 +646,7 @@ def retrieve_data(args, template, query_args=None, paginated=True):
647
646
  while True:
648
647
  # FIRST: Fetch response
649
648
 
650
- for attempt in range(MAX_RETRIES):
649
+ for attempt in range(args.max_retries + 1):
651
650
  request = _construct_request(
652
651
  per_page=per_page if paginated else None,
653
652
  query_args=query_args,
@@ -656,7 +655,7 @@ def retrieve_data(args, template, query_args=None, paginated=True):
656
655
  as_app=args.as_app,
657
656
  fine=args.token_fine is not None,
658
657
  )
659
- http_response = make_request_with_retry(request, auth)
658
+ http_response = make_request_with_retry(request, auth, args.max_retries)
660
659
 
661
660
  match http_response.getcode():
662
661
  case 200:
@@ -670,10 +669,10 @@ def retrieve_data(args, template, query_args=None, paginated=True):
670
669
  TimeoutError,
671
670
  ) as e:
672
671
  logger.warning(f"{type(e).__name__} reading response")
673
- if attempt < MAX_RETRIES - 1:
672
+ if attempt < args.max_retries:
674
673
  delay = calculate_retry_delay(attempt, {})
675
674
  logger.warning(
676
- f"Retrying in {delay:.1f}s (attempt {attempt + 1}/{MAX_RETRIES})"
675
+ f"Retrying read in {delay:.1f}s (attempt {attempt + 1}/{args.max_retries + 1})"
677
676
  )
678
677
  time.sleep(delay)
679
678
  continue # Next retry attempt
@@ -699,10 +698,10 @@ def retrieve_data(args, template, query_args=None, paginated=True):
699
698
  )
700
699
  else:
701
700
  logger.error(
702
- f"Failed to read response after {MAX_RETRIES} attempts for {next_url or template}"
701
+ f"Failed to read response after {args.max_retries + 1} attempts for {next_url or template}"
703
702
  )
704
703
  raise Exception(
705
- f"Failed to read response after {MAX_RETRIES} attempts for {next_url or template}"
704
+ f"Failed to read response after {args.max_retries + 1} attempts for {next_url or template}"
706
705
  )
707
706
 
708
707
  # SECOND: Process and paginate
@@ -734,7 +733,7 @@ def retrieve_data(args, template, query_args=None, paginated=True):
734
733
  return list(fetch_all())
735
734
 
736
735
 
737
- def make_request_with_retry(request, auth):
736
+ def make_request_with_retry(request, auth, max_retries=5):
738
737
  """Make HTTP request with automatic retry for transient errors."""
739
738
 
740
739
  def is_retryable_status(status_code, headers):
@@ -746,40 +745,49 @@ def make_request_with_retry(request, auth):
746
745
  return int(headers.get("x-ratelimit-remaining", 1)) < 1
747
746
  return False
748
747
 
749
- for attempt in range(MAX_RETRIES):
748
+ for attempt in range(max_retries + 1):
750
749
  try:
751
750
  return urlopen(request, context=https_ctx)
752
751
 
753
752
  except HTTPError as exc:
754
753
  # HTTPError can be used as a response-like object
755
754
  if not is_retryable_status(exc.code, exc.headers):
755
+ logger.error(
756
+ f"API Error: {exc.code} {exc.reason} for {request.full_url}"
757
+ )
756
758
  raise # Non-retryable error
757
759
 
758
- if attempt >= MAX_RETRIES - 1:
759
- logger.error(f"HTTP {exc.code} failed after {MAX_RETRIES} attempts")
760
+ if attempt >= max_retries:
761
+ logger.error(
762
+ f"HTTP {exc.code} failed after {max_retries + 1} attempts for {request.full_url}"
763
+ )
760
764
  raise
761
765
 
762
766
  delay = calculate_retry_delay(attempt, exc.headers)
763
767
  logger.warning(
764
- f"HTTP {exc.code}, retrying in {delay:.1f}s "
765
- f"(attempt {attempt + 1}/{MAX_RETRIES})"
768
+ f"HTTP {exc.code} ({exc.reason}), retrying in {delay:.1f}s "
769
+ f"(attempt {attempt + 1}/{max_retries + 1}) for {request.full_url}"
766
770
  )
767
771
  if auth is None and exc.code in (403, 429):
768
772
  logger.info("Hint: Authenticate to raise your GitHub rate limit")
769
773
  time.sleep(delay)
770
774
 
771
775
  except (URLError, socket.error) as e:
772
- if attempt >= MAX_RETRIES - 1:
773
- logger.error(f"Connection error failed after {MAX_RETRIES} attempts: {e}")
776
+ if attempt >= max_retries:
777
+ logger.error(
778
+ f"Connection error failed after {max_retries + 1} attempts: {e} for {request.full_url}"
779
+ )
774
780
  raise
775
781
  delay = calculate_retry_delay(attempt, {})
776
782
  logger.warning(
777
783
  f"Connection error: {e}, retrying in {delay:.1f}s "
778
- f"(attempt {attempt + 1}/{MAX_RETRIES})"
784
+ f"(attempt {attempt + 1}/{max_retries + 1}) for {request.full_url}"
779
785
  )
780
786
  time.sleep(delay)
781
787
 
782
- raise Exception(f"Request failed after {MAX_RETRIES} attempts") # pragma: no cover
788
+ raise Exception(
789
+ f"Request failed after {max_retries + 1} attempts"
790
+ ) # pragma: no cover
783
791
 
784
792
 
785
793
  def _construct_request(per_page, query_args, template, auth, as_app=None, fine=False):
@@ -1593,6 +1601,25 @@ def filter_repositories(args, unfiltered_repositories):
1593
1601
  ]
1594
1602
  if args.skip_archived:
1595
1603
  repositories = [r for r in repositories if not r.get("archived")]
1604
+ if args.starred_skip_size_over is not None:
1605
+ if args.starred_skip_size_over <= 0:
1606
+ logger.warning("--starred-skip-size-over must be greater than 0, ignoring")
1607
+ else:
1608
+ size_limit_kb = args.starred_skip_size_over * 1024
1609
+ filtered = []
1610
+ for r in repositories:
1611
+ if r.get("is_starred") and r.get("size", 0) > size_limit_kb:
1612
+ size_mb = r.get("size", 0) / 1024
1613
+ logger.info(
1614
+ "Skipping starred repo {0} ({1:.0f} MB) due to --starred-skip-size-over {2}".format(
1615
+ r.get("full_name", r.get("name")),
1616
+ size_mb,
1617
+ args.starred_skip_size_over,
1618
+ )
1619
+ )
1620
+ else:
1621
+ filtered.append(r)
1622
+ repositories = filtered
1596
1623
  if args.exclude:
1597
1624
  repositories = [
1598
1625
  r for r in repositories if "name" not in r or r["name"] not in args.exclude
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-backup
3
- Version: 0.58.0
3
+ Version: 0.60.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
@@ -66,23 +66,26 @@ Show the CLI help output::
66
66
 
67
67
  CLI Help output::
68
68
 
69
- github-backup [-h] [-u USERNAME] [-p PASSWORD] [-t TOKEN_CLASSIC]
70
- [-f TOKEN_FINE] [--as-app] [-o OUTPUT_DIRECTORY]
71
- [-l LOG_LEVEL] [-i] [--starred] [--all-starred]
72
- [--watched] [--followers] [--following] [--all] [--issues]
73
- [--issue-comments] [--issue-events] [--pulls]
69
+ github-backup [-h] [-t TOKEN_CLASSIC] [-f TOKEN_FINE] [-q] [--as-app]
70
+ [-o OUTPUT_DIRECTORY] [-l LOG_LEVEL] [-i]
71
+ [--incremental-by-files]
72
+ [--starred] [--all-starred] [--starred-skip-size-over MB]
73
+ [--watched] [--followers] [--following] [--all]
74
+ [--issues] [--issue-comments] [--issue-events] [--pulls]
74
75
  [--pull-comments] [--pull-commits] [--pull-details]
75
76
  [--labels] [--hooks] [--milestones] [--repositories]
76
- [--bare] [--lfs] [--wikis] [--gists] [--starred-gists]
77
- [--skip-archived] [--skip-existing] [-L [LANGUAGES ...]]
78
- [-N NAME_REGEX] [-H GITHUB_HOST] [-O] [-R REPOSITORY]
79
- [-P] [-F] [--prefer-ssh] [-v]
77
+ [--bare] [--no-prune] [--lfs] [--wikis] [--gists]
78
+ [--starred-gists] [--skip-archived] [--skip-existing]
79
+ [-L [LANGUAGES ...]] [-N NAME_REGEX] [-H GITHUB_HOST]
80
+ [-O] [-R REPOSITORY] [-P] [-F] [--prefer-ssh] [-v]
80
81
  [--keychain-name OSX_KEYCHAIN_ITEM_NAME]
81
82
  [--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT]
82
83
  [--releases] [--latest-releases NUMBER_OF_LATEST_RELEASES]
83
- [--skip-prerelease] [--assets] [--skip-assets-on [REPO ...]]
84
- [--attachments] [--exclude [REPOSITORY [REPOSITORY ...]]
85
- [--throttle-limit THROTTLE_LIMIT] [--throttle-pause THROTTLE_PAUSE]
84
+ [--skip-prerelease] [--assets]
85
+ [--skip-assets-on [SKIP_ASSETS_ON ...]] [--attachments]
86
+ [--throttle-limit THROTTLE_LIMIT]
87
+ [--throttle-pause THROTTLE_PAUSE]
88
+ [--exclude [EXCLUDE ...]]
86
89
  USER
87
90
 
88
91
  Backup a github account
@@ -90,29 +93,29 @@ CLI Help output::
90
93
  positional arguments:
91
94
  USER github username
92
95
 
93
- optional arguments:
96
+ options:
94
97
  -h, --help show this help message and exit
95
- -u USERNAME, --username USERNAME
96
- username for basic auth
97
- -p PASSWORD, --password PASSWORD
98
- password for basic auth. If a username is given but
99
- not a password, the password will be prompted for.
100
- -f TOKEN_FINE, --token-fine TOKEN_FINE
101
- fine-grained personal access token or path to token
102
- (file://...)
103
- -t TOKEN_CLASSIC, --token TOKEN_CLASSIC
98
+ -t, --token TOKEN_CLASSIC
104
99
  personal access, OAuth, or JSON Web token, or path to
105
100
  token (file://...)
101
+ -f, --token-fine TOKEN_FINE
102
+ fine-grained personal access token (github_pat_....),
103
+ or path to token (file://...)
104
+ -q, --quiet supress log messages less severe than warning, e.g.
105
+ info
106
106
  --as-app authenticate as github app instead of as a user.
107
- -o OUTPUT_DIRECTORY, --output-directory OUTPUT_DIRECTORY
107
+ -o, --output-directory OUTPUT_DIRECTORY
108
108
  directory at which to backup the repositories
109
- -l LOG_LEVEL, --log-level LOG_LEVEL
109
+ -l, --log-level LOG_LEVEL
110
110
  log level to use (default: info, possible levels:
111
111
  debug, info, warning, error, critical)
112
112
  -i, --incremental incremental backup
113
- --incremental-by-files incremental backup using modified time of files
113
+ --incremental-by-files
114
+ incremental backup based on modification date of files
114
115
  --starred include JSON output of starred repositories in backup
115
116
  --all-starred include starred repositories in backup [*]
117
+ --starred-skip-size-over MB
118
+ skip starred repositories larger than this size in MB
116
119
  --watched include JSON output of watched repositories in backup
117
120
  --followers include JSON output of followers in backup
118
121
  --following include JSON output of following users in backup
@@ -130,20 +133,22 @@ CLI Help output::
130
133
  --milestones include milestones in backup
131
134
  --repositories include repository clone in backup
132
135
  --bare clone bare repositories
136
+ --no-prune disable prune option for git fetch
133
137
  --lfs clone LFS repositories (requires Git LFS to be
134
138
  installed, https://git-lfs.github.com) [*]
135
139
  --wikis include wiki clone in backup
136
140
  --gists include gists in backup [*]
137
141
  --starred-gists include starred gists in backup [*]
142
+ --skip-archived skip project if it is archived
138
143
  --skip-existing skip project if a backup directory exists
139
- -L [LANGUAGES [LANGUAGES ...]], --languages [LANGUAGES [LANGUAGES ...]]
144
+ -L, --languages [LANGUAGES ...]
140
145
  only allow these languages
141
- -N NAME_REGEX, --name-regex NAME_REGEX
146
+ -N, --name-regex NAME_REGEX
142
147
  python regex to match names against
143
- -H GITHUB_HOST, --github-host GITHUB_HOST
148
+ -H, --github-host GITHUB_HOST
144
149
  GitHub Enterprise hostname
145
150
  -O, --organization whether or not this is an organization user
146
- -R REPOSITORY, --repository REPOSITORY
151
+ -R, --repository REPOSITORY
147
152
  name of repository to limit backup to
148
153
  -P, --private include private repositories [*]
149
154
  -F, --fork include forked repositories [*]
@@ -158,19 +163,16 @@ CLI Help output::
158
163
  --releases include release information, not including assets or
159
164
  binaries
160
165
  --latest-releases NUMBER_OF_LATEST_RELEASES
161
- include certain number of the latest releases;
162
- only applies if including releases
163
- --skip-prerelease skip prerelease and draft versions; only applies if including releases
166
+ include certain number of the latest releases; only
167
+ applies if including releases
168
+ --skip-prerelease skip prerelease and draft versions; only applies if
169
+ including releases
164
170
  --assets include assets alongside release information; only
165
171
  applies if including releases
166
- --skip-assets-on [REPO ...]
167
- skip asset downloads for these repositories (e.g.
168
- --skip-assets-on repo1 owner/repo2)
169
- --attachments download user-attachments from issues and pull requests
170
- to issues/attachments/{issue_number}/ and
171
- pulls/attachments/{pull_number}/ directories
172
- --exclude [REPOSITORY [REPOSITORY ...]]
173
- names of repositories to exclude from backup.
172
+ --skip-assets-on [SKIP_ASSETS_ON ...]
173
+ skip asset downloads for these repositories
174
+ --attachments download user-attachments from issues and pull
175
+ requests
174
176
  --throttle-limit THROTTLE_LIMIT
175
177
  start throttling of GitHub API requests after this
176
178
  amount of API requests remain
@@ -178,7 +180,10 @@ CLI Help output::
178
180
  wait this amount of seconds when API request
179
181
  throttling is active (default: 30.0, requires
180
182
  --throttle-limit to be set)
181
-
183
+ --exclude [EXCLUDE ...]
184
+ names of repositories to exclude
185
+ --retries MAX_RETRIES
186
+ maximum number of retries for API calls (default: 5)
182
187
 
183
188
  Usage Details
184
189
  =============
@@ -186,13 +191,13 @@ Usage Details
186
191
  Authentication
187
192
  --------------
188
193
 
189
- **Password-based authentication** will fail if you have two-factor authentication enabled, and will `be deprecated <https://github.blog/2023-03-09-raising-the-bar-for-software-security-github-2fa-begins-march-13/>`_ by 2023 EOY.
194
+ GitHub requires token-based authentication for API access. Password authentication was `removed in November 2020 <https://developer.github.com/changes/2020-02-14-deprecating-password-auth/>`_.
190
195
 
191
- ``--username`` is used for basic password authentication and separate from the positional argument ``USER``, which specifies the user account you wish to back up.
196
+ The positional argument ``USER`` specifies the user or organization account you wish to back up.
192
197
 
193
- **Classic tokens** are `slightly less secure <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#personal-access-tokens-classic>`_ as they provide very coarse-grained permissions.
198
+ **Fine-grained tokens** (``-f TOKEN_FINE``) are recommended for most use cases, especially long-running backups (e.g. cron jobs), as they provide precise permission control.
194
199
 
195
- If you need authentication for long-running backups (e.g. for a cron job) it is recommended to use **fine-grained personal access token** ``-f TOKEN_FINE``.
200
+ **Classic tokens** (``-t TOKEN``) are `slightly less secure <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#personal-access-tokens-classic>`_ as they provide very coarse-grained permissions.
196
201
 
197
202
 
198
203
  Fine Tokens
@@ -320,10 +325,20 @@ All is not everything
320
325
 
321
326
  The ``--all`` argument does not include: cloning private repos (``-P, --private``), cloning forks (``-F, --fork``), cloning starred repositories (``--all-starred``), ``--pull-details``, cloning LFS repositories (``--lfs``), cloning gists (``--gists``) or cloning starred gist repos (``--starred-gists``). See examples for more.
322
327
 
323
- Cloning all starred size
324
- ------------------------
328
+ Starred repository size
329
+ -----------------------
330
+
331
+ Using the ``--all-starred`` argument to clone all starred repositories may use a large amount of storage space.
332
+
333
+ To see your starred repositories sorted by size (requires `GitHub CLI <https://cli.github.com>`_)::
334
+
335
+ gh api user/starred --paginate --jq 'sort_by(-.size)[]|"\(.full_name) \(.size/1024|round)MB"'
336
+
337
+ To limit which starred repositories are cloned, use ``--starred-skip-size-over SIZE`` where SIZE is in MB. For example, ``--starred-skip-size-over 500`` will skip any starred repository where the git repository size (code and history) exceeds 500 MB. Note that this size limit only applies to the repository itself, not issues, release assets or other metadata. This filter only affects starred repositories; your own repositories are always included regardless of size.
338
+
339
+ For finer control, avoid using ``--assets`` with starred repos, or use ``--skip-assets-on`` for specific repositories with large release binaries.
325
340
 
326
- Using the ``--all-starred`` argument to clone all starred repositories may use a large amount of storage space, especially if ``--all`` or more arguments are used. e.g. commonly starred repos can have tens of thousands of issues, many large assets and the repo itself etc. Consider just storing links to starred repos in JSON format with ``--starred``.
341
+ Alternatively, consider just storing links to starred repos in JSON format with ``--starred``.
327
342
 
328
343
  Incremental Backup
329
344
  ------------------
@@ -0,0 +1,11 @@
1
+ github_backup/__init__.py,sha256=XCDU0PzRSGNchH5cFRqniZdytnPgXmacSgzeC0BxXiI,23
2
+ github_backup/__main__.py,sha256=3yorCiC63maaxbjdtaeAs-G-yAAxKSO3mprhGfa1Fho,271
3
+ github_backup/cli.py,sha256=ifnOne5AaEI7laofgIq3geURJtvbA-bzxSQgLcmZgd0,2345
4
+ github_backup/github_backup.py,sha256=DJ1mFWELLAZBbM8UXmsHOxin3OBGFIFZk_SXQdhv_7w,82562
5
+ github_backup-0.60.0.dist-info/licenses/LICENSE.txt,sha256=ML-pLyn1qtAKo_DHMcQ8-p7k3f8cp-IR-mf5B49R2rk,1085
6
+ github_backup-0.60.0.dist-info/METADATA,sha256=Lwu1h4UYm6z8OUq9iieqgS_7ITlsB-oQujHB0h302PM,26529
7
+ github_backup-0.60.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ github_backup-0.60.0.dist-info/entry_points.txt,sha256=cLPKK7MqC77QVb6HnT2mnyL_f-ciavJG_Y9vVHlCF68,57
9
+ github_backup-0.60.0.dist-info/top_level.txt,sha256=Za6fKRAlxZ0b3ZBH_osPqklLe14LetdNAkvAQANT2AA,14
10
+ github_backup-0.60.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
11
+ github_backup-0.60.0.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- github_backup/__init__.py,sha256=oIkJTCdzxBkcFHWQdIvMrmJih4tpnF1BnfoNPd1mB48,23
2
- github_backup/__main__.py,sha256=3yorCiC63maaxbjdtaeAs-G-yAAxKSO3mprhGfa1Fho,271
3
- github_backup/cli.py,sha256=zMUU0pTnh9KPqv8K--KNpUNf-yaA1qWeI6AdC1KVrn8,2361
4
- github_backup/github_backup.py,sha256=Q3EcCwO7X8v5MPdWilx0zsJa8FLUINzSI8bjHw08O50,81287
5
- github_backup-0.58.0.dist-info/licenses/LICENSE.txt,sha256=ML-pLyn1qtAKo_DHMcQ8-p7k3f8cp-IR-mf5B49R2rk,1085
6
- github_backup-0.58.0.dist-info/METADATA,sha256=wYRqf6V7hjtir_td4uYuK296O5KHeqpCPGhsr-ndnK8,25921
7
- github_backup-0.58.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- github_backup-0.58.0.dist-info/entry_points.txt,sha256=cLPKK7MqC77QVb6HnT2mnyL_f-ciavJG_Y9vVHlCF68,57
9
- github_backup-0.58.0.dist-info/top_level.txt,sha256=Za6fKRAlxZ0b3ZBH_osPqklLe14LetdNAkvAQANT2AA,14
10
- github_backup-0.58.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
11
- github_backup-0.58.0.dist-info/RECORD,,