Compare commits

..

15 Commits

Author SHA1 Message Date
Jose Diaz-Gonzalez
07e32b186c Release version 0.42.0 2022-11-28 00:25:13 -05:00
josegonzalez
dcc90b747a Merge pull request #204 from Assimila/exclude_repositories
Add option to exclude repositories
2022-11-28 00:23:20 -05:00
josegonzalez
f414fac108 Merge pull request #202 from TheOneric/pull_backup-regular-comments
Backup regular pull request comments as well
2022-11-28 00:23:08 -05:00
josegonzalez
38692bc836 Merge pull request #201 from TRAdEWORKS/fix-bug-request-url-error-forever-retry
Fix a bug forever retry when request url error
2022-11-28 00:22:54 -05:00
josegonzalez
81362e5596 Merge pull request #200 from TRAdEWORKS/no-prune-flag
Add --no-prune command line argument to disable prune option when doing git fetch
2022-11-28 00:22:40 -05:00
npounder
753a26d0d6 add option to exclude repositories 2022-11-25 12:35:24 +00:00
Oneric
b629a865f4 Backup regular pull request comments as well
Before, only review comments were backed up;
regular comments need to be fetched via issue API.

Fixes: https://github.com/josegonzalez/python-github-backup/issues/150
2022-07-12 18:38:11 +02:00
kornpisey
75ec773a6f fix bug forever retry when request url error 2022-05-30 13:50:23 +09:00
kornpisey
f8a16ee0f8 added --no-prune option to disable prune option when doing git fetch 2022-05-30 13:46:41 +09:00
Jose Diaz-Gonzalez
63441ebfbc Release version 0.41.0 2022-03-02 02:36:41 -05:00
Jose Diaz-Gonzalez
7ad324225e Merge pull request #191 from SkySoft-ATM/bug/lfs_mirror
git lfs clone does not respect --mirror
2022-03-02 02:34:17 -05:00
Louis Parisot
885e94a102 git lfs clone doe snot respect --mirror 2022-02-03 11:45:59 +01:00
Jose Diaz-Gonzalez
9e1800f56e Release version 0.40.2 2021-12-29 12:49:10 -05:00
Jose Diaz-Gonzalez
d057ee0d04 Merge pull request #186 from atinary-afoulon/patch-1
Fix lint issues raised by Flake8
2021-12-29 12:48:30 -05:00
atinary-afoulon
64562f2460 Fix lint issues raised by Flake8
According to job: 
[ https://app.circleci.com/pipelines/github/josegonzalez/python-github-backup/30/workflows/74eb93f2-2505-435d-b728-03b3cc04c14a/jobs/23 ]

Failed on the following checks:
./github_backup/github_backup.py:20:1: F811 redefinition of unused 'logging' from line 14
./github_backup/github_backup.py:45:1: E302 expected 2 blank lines, found 1
./github_backup/github_backup.py:136:20: E251 unexpected spaces around keyword / parameter equals
2021-12-13 14:33:21 +01:00
3 changed files with 46 additions and 18 deletions

View File

@@ -41,8 +41,8 @@ CLI Usage is as follows::
[-P] [-F] [--prefer-ssh] [-v] [-P] [-F] [--prefer-ssh] [-v]
[--keychain-name OSX_KEYCHAIN_ITEM_NAME] [--keychain-name OSX_KEYCHAIN_ITEM_NAME]
[--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT] [--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT]
[--releases] [--assets] [--throttle-limit THROTTLE_LIMIT] [--releases] [--assets] [--exclude [REPOSITORY [REPOSITORY ...]]
[--throttle-pause THROTTLE_PAUSE] [--throttle-limit THROTTLE_LIMIT] [--throttle-pause THROTTLE_PAUSE]
USER USER
Backup a github account Backup a github account
@@ -112,6 +112,8 @@ CLI Usage is as follows::
binaries binaries
--assets include assets alongside release information; only --assets include assets alongside release information; only
applies if including releases applies if including releases
--exclude [REPOSITORY [REPOSITORY ...]]
names of repositories to exclude from backup.
--throttle-limit THROTTLE_LIMIT --throttle-limit THROTTLE_LIMIT
start throttling of GitHub API requests after this start throttling of GitHub API requests after this
amount of API requests remain amount of API requests remain

View File

@@ -1 +1 @@
__version__ = '0.40.1' __version__ = '0.42.0'

View File

@@ -11,7 +11,6 @@ import datetime
import errno import errno
import getpass import getpass
import json import json
import logging
import os import os
import re import re
import select import select
@@ -42,6 +41,7 @@ FNULL = open(os.devnull, 'w')
def _get_log_date(): def _get_log_date():
return datetime.datetime.isoformat(datetime.datetime.now()) return datetime.datetime.isoformat(datetime.datetime.now())
def log_info(message): def log_info(message):
""" """
Log message (str) or messages (List[str]) to stdout Log message (str) or messages (List[str]) to stdout
@@ -133,7 +133,7 @@ def mask_password(url, secret='*****'):
return url.replace(parsed.password, secret) return url.replace(parsed.password, secret)
def parse_args(args = None): def parse_args(args=None):
parser = argparse.ArgumentParser(description='Backup a github account') parser = argparse.ArgumentParser(description='Backup a github account')
parser.add_argument('user', parser.add_argument('user',
metavar='USER', metavar='USER',
@@ -239,6 +239,10 @@ def parse_args(args = None):
action='store_true', action='store_true',
dest='bare_clone', dest='bare_clone',
help='clone bare repositories') help='clone bare repositories')
parser.add_argument('--no-prune',
action='store_true',
dest='no_prune',
help='disable prune option for git fetch')
parser.add_argument('--lfs', parser.add_argument('--lfs',
action='store_true', action='store_true',
dest='lfs_clone', dest='lfs_clone',
@@ -324,6 +328,10 @@ def parse_args(args = None):
type=float, type=float,
default=30.0, default=30.0,
help='wait this amount of seconds when API request throttling is active (default: 30.0, requires --throttle-limit to be set)') help='wait this amount of seconds when API request throttling is active (default: 30.0, requires --throttle-limit to be set)')
parser.add_argument('--exclude',
dest='exclude',
help='names of repositories to exclude',
nargs="*")
return parser.parse_args(args) return parser.parse_args(args)
@@ -533,12 +541,12 @@ def _get_response(request, auth, template):
r = exc r = exc
except URLError as e: except URLError as e:
log_warning(e.reason) log_warning(e.reason)
should_continue = _request_url_error(template, retry_timeout) should_continue, retry_timeout = _request_url_error(template, retry_timeout)
if not should_continue: if not should_continue:
raise raise
except socket.error as e: except socket.error as e:
log_warning(e.strerror) log_warning(e.strerror)
should_continue = _request_url_error(template, retry_timeout) should_continue, retry_timeout = _request_url_error(template, retry_timeout)
if not should_continue: if not should_continue:
raise raise
@@ -598,16 +606,15 @@ def _request_http_error(exc, auth, errors):
def _request_url_error(template, retry_timeout): def _request_url_error(template, retry_timeout):
# Incase of a connection timing out, we can retry a few time # In case of a connection timing out, we can retry a few time
# But we won't crash and not back-up the rest now # But we won't crash and not back-up the rest now
log_info('{} timed out'.format(template)) log_info('{} timed out'.format(template))
retry_timeout -= 1 retry_timeout -= 1
if retry_timeout >= 0: if retry_timeout >= 0:
return True return True, retry_timeout
raise Exception('{} timed out to much, skipping!') raise Exception('{} timed out to much, skipping!')
return False
class S3HTTPRedirectHandler(HTTPRedirectHandler): class S3HTTPRedirectHandler(HTTPRedirectHandler):
@@ -750,6 +757,8 @@ def filter_repositories(args, unfiltered_repositories):
repositories = [r for r in repositories if name_regex.match(r['name'])] repositories = [r for r in repositories if name_regex.match(r['name'])]
if args.skip_archived: if args.skip_archived:
repositories = [r for r in repositories if not r.get('archived')] repositories = [r for r in repositories if not r.get('archived')]
if args.exclude:
repositories = [r for r in repositories if r['name'] not in args.exclude]
return repositories return repositories
@@ -790,7 +799,8 @@ def backup_repositories(args, output_directory, repositories):
repo_dir, repo_dir,
skip_existing=args.skip_existing, skip_existing=args.skip_existing,
bare_clone=args.bare_clone, bare_clone=args.bare_clone,
lfs_clone=args.lfs_clone) lfs_clone=args.lfs_clone,
no_prune=args.no_prune)
if repository.get('is_gist'): if repository.get('is_gist'):
# dump gist information to a file as well # dump gist information to a file as well
@@ -807,8 +817,9 @@ def backup_repositories(args, output_directory, repositories):
os.path.join(repo_cwd, 'wiki'), os.path.join(repo_cwd, 'wiki'),
skip_existing=args.skip_existing, skip_existing=args.skip_existing,
bare_clone=args.bare_clone, bare_clone=args.bare_clone,
lfs_clone=args.lfs_clone) lfs_clone=args.lfs_clone,
no_prune=args.no_prune
)
if args.include_issues or args.include_everything: if args.include_issues or args.include_everything:
backup_issues(args, repo_cwd, repository, repos_template) backup_issues(args, repo_cwd, repository, repos_template)
@@ -902,6 +913,8 @@ def backup_pulls(args, repo_cwd, repository, repos_template):
pulls = {} pulls = {}
_pulls_template = '{0}/{1}/pulls'.format(repos_template, _pulls_template = '{0}/{1}/pulls'.format(repos_template,
repository['full_name']) repository['full_name'])
_issue_template = '{0}/{1}/issues'.format(repos_template,
repository['full_name'])
query_args = { query_args = {
'filter': 'all', 'filter': 'all',
'state': 'all', 'state': 'all',
@@ -941,10 +954,17 @@ def backup_pulls(args, repo_cwd, repository, repos_template):
log_info('Saving {0} pull requests to disk'.format( log_info('Saving {0} pull requests to disk'.format(
len(list(pulls.keys())))) len(list(pulls.keys()))))
# Comments from pulls API are only _review_ comments
# regular comments need to be fetched via issue API.
# For backwards compatibility with versions <= 0.41.0
# keep name "comment_data" for review comments
comments_regular_template = _issue_template + '/{0}/comments'
comments_template = _pulls_template + '/{0}/comments' comments_template = _pulls_template + '/{0}/comments'
commits_template = _pulls_template + '/{0}/commits' commits_template = _pulls_template + '/{0}/commits'
for number, pull in list(pulls.items()): for number, pull in list(pulls.items()):
if args.include_pull_comments or args.include_everything: if args.include_pull_comments or args.include_everything:
template = comments_regular_template.format(number)
pulls[number]['comment_regular_data'] = retrieve_data(args, template)
template = comments_template.format(number) template = comments_template.format(number)
pulls[number]['comment_data'] = retrieve_data(args, template) pulls[number]['comment_data'] = retrieve_data(args, template)
if args.include_pull_commits or args.include_everything: if args.include_pull_commits or args.include_everything:
@@ -1053,7 +1073,8 @@ def fetch_repository(name,
local_dir, local_dir,
skip_existing=False, skip_existing=False,
bare_clone=False, bare_clone=False,
lfs_clone=False): lfs_clone=False,
no_prune=False):
if bare_clone: if bare_clone:
if os.path.exists(local_dir): if os.path.exists(local_dir):
clone_exists = subprocess.check_output(['git', clone_exists = subprocess.check_output(['git',
@@ -1099,6 +1120,8 @@ def fetch_repository(name,
git_command = ['git', 'lfs', 'fetch', '--all', '--prune'] git_command = ['git', 'lfs', 'fetch', '--all', '--prune']
else: else:
git_command = ['git', 'fetch', '--all', '--force', '--tags', '--prune'] git_command = ['git', 'fetch', '--all', '--force', '--tags', '--prune']
if no_prune:
git_command.pop()
logging_subprocess(git_command, None, cwd=local_dir) logging_subprocess(git_command, None, cwd=local_dir)
else: else:
log_info('Cloning {0} repository from {1} to {2}'.format( log_info('Cloning {0} repository from {1} to {2}'.format(
@@ -1106,10 +1129,13 @@ def fetch_repository(name,
masked_remote_url, masked_remote_url,
local_dir)) local_dir))
if bare_clone: if bare_clone:
if lfs_clone:
git_command = ['git', 'lfs', 'clone', '--mirror', remote_url, local_dir]
else:
git_command = ['git', 'clone', '--mirror', remote_url, local_dir] git_command = ['git', 'clone', '--mirror', remote_url, local_dir]
logging_subprocess(git_command, None)
if lfs_clone:
git_command = ['git', 'lfs', 'fetch', '--all', '--prune']
if no_prune:
git_command.pop()
logging_subprocess(git_command, None, cwd=local_dir)
else: else:
if lfs_clone: if lfs_clone:
git_command = ['git', 'lfs', 'clone', remote_url, local_dir] git_command = ['git', 'lfs', 'clone', remote_url, local_dir]