| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,38 +2,41 @@ | |||
| 2 | 2 | ||
| 3 | 3 | import os, sys, logging | |
| 4 | 4 | ||
| 5 | + logging.basicConfig( | ||
| 6 | + format='%(asctime)s.%(msecs)03d: %(message)s', | ||
| 7 | + datefmt='%Y-%m-%dT%H:%M:%S', | ||
| 8 | + level=logging.INFO | ||
| 9 | + ) | ||
| 10 | + | ||
| 5 | 11 | from github_backup.github_backup import ( | |
| 6 | 12 | backup_account, | |
| 7 | 13 | backup_repositories, | |
| 8 | 14 | check_git_lfs_install, | |
| 9 | 15 | filter_repositories, | |
| 10 | 16 | get_authenticated_user, | |
| 11 | - log_info, | ||
| 12 | - log_warning, | ||
| 17 | + logger, | ||
| 13 | 18 | mkdir_p, | |
| 14 | 19 | parse_args, | |
| 15 | 20 | retrieve_repositories, | |
| 16 | 21 | ) | |
| 17 | 22 | ||
| 18 | - logging.basicConfig( | ||
| 19 | - format='%(asctime)s.%(msecs)03d: %(message)s', | ||
| 20 | - datefmt='%Y-%m-%dT%H:%M:%S', | ||
| 21 | - level=logging.INFO | ||
| 22 | - ) | ||
| 23 | 23 | ||
| 24 | 24 | def main(): | |
| 25 | 25 | args = parse_args() | |
| 26 | 26 | ||
| 27 | + if args.quiet: | ||
| 28 | + logger.setLevel(logging.WARNING) | ||
| 29 | + | ||
| 27 | 30 | output_directory = os.path.realpath(args.output_directory) | |
| 28 | 31 | if not os.path.isdir(output_directory): | |
| 29 | - log_info('Create output directory {0}'.format(output_directory)) | ||
| 32 | + logger.info('Create output directory {0}'.format(output_directory)) | ||
| 30 | 33 | mkdir_p(output_directory) | |
| 31 | 34 | ||
| 32 | 35 | if args.lfs_clone: | |
| 33 | 36 | check_git_lfs_install() | |
| 34 | 37 | ||
| 35 | 38 | if not args.as_app: | |
| 36 | - log_info('Backing up user {0} to {1}'.format(args.user, output_directory)) | ||
| 39 | + logger.info('Backing up user {0} to {1}'.format(args.user, output_directory)) | ||
| 37 | 40 | authenticated_user = get_authenticated_user(args) | |
| 38 | 41 | else: | |
| 39 | 42 | authenticated_user = {'login': None} | |
@@ -48,5 +51,5 @@ if __name__ == '__main__': | |||
| 48 | 51 | try: | |
| 49 | 52 | main() | |
| 50 | 53 | except Exception as e: | |
| 51 | - log_warning(str(e)) | ||
| 54 | + logger.warning(str(e)) | ||
| 52 | 55 | sys.exit(1) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,7 +7,6 @@ | |||
| 7 | 7 | import base64 | |
| 8 | 8 | import calendar | |
| 9 | 9 | import codecs | |
| 10 | - import datetime | ||
| 11 | 10 | import errno | |
| 12 | 11 | import getpass | |
| 13 | 12 | import json | |
@@ -37,31 +36,7 @@ | |||
| 37 | 36 | ||
| 38 | 37 | FNULL = open(os.devnull, 'w') | |
| 39 | 38 | ||
| 40 | - | ||
| 41 | - def _get_log_date(): | ||
| 42 | - return datetime.datetime.isoformat(datetime.datetime.now()) | ||
| 43 | - | ||
| 44 | - | ||
| 45 | - def log_info(message): | ||
| 46 | - """ | ||
| 47 | - Log message (str) or messages (List[str]) to stdout | ||
| 48 | - """ | ||
| 49 | - if type(message) == str: | ||
| 50 | - message = [message] | ||
| 51 | - | ||
| 52 | - for msg in message: | ||
| 53 | - logging.info(msg) | ||
| 54 | - | ||
| 55 | - | ||
| 56 | - def log_warning(message): | ||
| 57 | - """ | ||
| 58 | - Log message (str) or messages (List[str]) to stderr | ||
| 59 | - """ | ||
| 60 | - if type(message) == str: | ||
| 61 | - message = [message] | ||
| 62 | - | ||
| 63 | - for msg in message: | ||
| 64 | - logging.warning(msg) | ||
| 39 | + logger = logging.getLogger(__name__) | ||
| 65 | 40 | ||
| 66 | 41 | ||
| 67 | 42 | def logging_subprocess(popenargs, | |
@@ -77,7 +52,7 @@ def logging_subprocess(popenargs, | |||
| 77 | 52 | child = subprocess.Popen(popenargs, stdout=subprocess.PIPE, | |
| 78 | 53 | stderr=subprocess.PIPE, **kwargs) | |
| 79 | 54 | if sys.platform == 'win32': | |
| 80 | - log_info("Windows operating system detected - no subprocess logging will be returned") | ||
| 55 | + logger.info("Windows operating system detected - no subprocess logging will be returned") | ||
| 81 | 56 | ||
| 82 | 57 | log_level = {child.stdout: stdout_log_level, | |
| 83 | 58 | child.stderr: stderr_log_level} | |
@@ -139,6 +114,11 @@ def parse_args(args=None): | |||
| 139 | 114 | metavar='USER', | |
| 140 | 115 | type=str, | |
| 141 | 116 | help='github username') | |
| 117 | + parser.add_argument('-q', | ||
| 118 | + '--quiet', | ||
| 119 | + action='store_true', | ||
| 120 | + dest='quiet', | ||
| 121 | + help='supress non-error log messages') | ||
| 142 | 122 | parser.add_argument('-u', | |
| 143 | 123 | '--username', | |
| 144 | 124 | dest='username', | |
@@ -441,29 +421,29 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False): | |||
| 441 | 421 | try: | |
| 442 | 422 | response = json.loads(r.read().decode('utf-8')) | |
| 443 | 423 | except IncompleteRead: | |
| 444 | - log_warning("Incomplete read error detected") | ||
| 424 | + logger.warning("Incomplete read error detected") | ||
| 445 | 425 | read_error = True | |
| 446 | 426 | except json.decoder.JSONDecodeError: | |
| 447 | - log_warning("JSON decode error detected") | ||
| 427 | + logger.warning("JSON decode error detected") | ||
| 448 | 428 | read_error = True | |
| 449 | 429 | except TimeoutError: | |
| 450 | - log_warning("Tiemout error detected") | ||
| 430 | + logger.warning("Tiemout error detected") | ||
| 451 | 431 | read_error = True | |
| 452 | 432 | else: | |
| 453 | 433 | read_error = False | |
| 454 | 434 | ||
| 455 | 435 | # be gentle with API request limit and throttle requests if remaining requests getting low | |
| 456 | 436 | limit_remaining = int(r.headers.get('x-ratelimit-remaining', 0)) | |
| 457 | 437 | if args.throttle_limit and limit_remaining <= args.throttle_limit: | |
| 458 | - log_info( | ||
| 438 | + logger.info( | ||
| 459 | 439 | 'API request limit hit: {} requests left, pausing further requests for {}s'.format( | |
| 460 | 440 | limit_remaining, | |
| 461 | 441 | args.throttle_pause)) | |
| 462 | 442 | time.sleep(args.throttle_pause) | |
| 463 | 443 | ||
| 464 | 444 | retries = 0 | |
| 465 | 445 | while retries < 3 and (status_code == 502 or read_error): | |
| 466 | - log_warning('API request failed. Retrying in 5 seconds') | ||
| 446 | + logger.warning('API request failed. Retrying in 5 seconds') | ||
| 467 | 447 | retries += 1 | |
| 468 | 448 | time.sleep(5) | |
| 469 | 449 | request = _construct_request(per_page, page, query_args, template, auth, as_app=args.as_app) # noqa | |
@@ -474,13 +454,13 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False): | |||
| 474 | 454 | response = json.loads(r.read().decode('utf-8')) | |
| 475 | 455 | read_error = False | |
| 476 | 456 | except IncompleteRead: | |
| 477 | - log_warning("Incomplete read error detected") | ||
| 457 | + logger.warning("Incomplete read error detected") | ||
| 478 | 458 | read_error = True | |
| 479 | 459 | except json.decoder.JSONDecodeError: | |
| 480 | - log_warning("JSON decode error detected") | ||
| 460 | + logger.warning("JSON decode error detected") | ||
| 481 | 461 | read_error = True | |
| 482 | 462 | except TimeoutError: | |
| 483 | - log_warning("Tiemout error detected") | ||
| 463 | + logger.warning("Tiemout error detected") | ||
| 484 | 464 | read_error = True | |
| 485 | 465 | ||
| 486 | 466 | if status_code != 200: | |
@@ -532,12 +512,12 @@ def _get_response(request, auth, template): | |||
| 532 | 512 | errors, should_continue = _request_http_error(exc, auth, errors) # noqa | |
| 533 | 513 | r = exc | |
| 534 | 514 | except URLError as e: | |
| 535 | - log_warning(e.reason) | ||
| 515 | + logger.warning(e.reason) | ||
| 536 | 516 | should_continue = _request_url_error(template, retry_timeout) | |
| 537 | 517 | if not should_continue: | |
| 538 | 518 | raise | |
| 539 | 519 | except socket.error as e: | |
| 540 | - log_warning(e.strerror) | ||
| 520 | + logger.warning(e.strerror) | ||
| 541 | 521 | should_continue = _request_url_error(template, retry_timeout) | |
| 542 | 522 | if not should_continue: | |
| 543 | 523 | raise | |
@@ -563,7 +543,7 @@ def _construct_request(per_page, page, query_args, template, auth, as_app=None): | |||
| 563 | 543 | auth = auth.encode('ascii') | |
| 564 | 544 | request.add_header('Authorization', 'token '.encode('ascii') + auth) | |
| 565 | 545 | request.add_header('Accept', 'application/vnd.github.machine-man-preview+json') | |
| 566 | - log_info('Requesting {}?{}'.format(template, querystring)) | ||
| 546 | + logger.info('Requesting {}?{}'.format(template, querystring)) | ||
| 567 | 547 | return request | |
| 568 | 548 | ||
| 569 | 549 | ||
@@ -587,10 +567,10 @@ def _request_http_error(exc, auth, errors): | |||
| 587 | 567 | delta = max(10, reset - gm_now) | |
| 588 | 568 | ||
| 589 | 569 | limit = headers.get('x-ratelimit-limit') | |
| 590 | - log_warning('Exceeded rate limit of {} requests; waiting {} seconds to reset'.format(limit, delta)) # noqa | ||
| 570 | + logger.warning('Exceeded rate limit of {} requests; waiting {} seconds to reset'.format(limit, delta)) # noqa | ||
| 591 | 571 | ||
| 592 | 572 | if auth is None: | |
| 593 | - log_info('Hint: Authenticate to raise your GitHub rate limit') | ||
| 573 | + logger.info('Hint: Authenticate to raise your GitHub rate limit') | ||
| 594 | 574 | ||
| 595 | 575 | time.sleep(delta) | |
| 596 | 576 | should_continue = True | |
@@ -600,7 +580,7 @@ def _request_http_error(exc, auth, errors): | |||
| 600 | 580 | def _request_url_error(template, retry_timeout): | |
| 601 | 581 | # Incase of a connection timing out, we can retry a few time | |
| 602 | 582 | # But we won't crash and not back-up the rest now | |
| 603 | - log_info('{} timed out'.format(template)) | ||
| 583 | + logger.info('{} timed out'.format(template)) | ||
| 604 | 584 | retry_timeout -= 1 | |
| 605 | 585 | ||
| 606 | 586 | if retry_timeout >= 0: | |
@@ -645,14 +625,14 @@ def download_file(url, path, auth): | |||
| 645 | 625 | f.write(chunk) | |
| 646 | 626 | except HTTPError as exc: | |
| 647 | 627 | # Gracefully handle 404 responses (and others) when downloading from S3 | |
| 648 | - log_warning('Skipping download of asset {0} due to HTTPError: {1}'.format(url, exc.reason)) | ||
| 628 | + logger.warning('Skipping download of asset {0} due to HTTPError: {1}'.format(url, exc.reason)) | ||
| 649 | 629 | except URLError as e: | |
| 650 | 630 | # Gracefully handle other URL errors | |
| 651 | - log_warning('Skipping download of asset {0} due to URLError: {1}'.format(url, e.reason)) | ||
| 631 | + logger.warning('Skipping download of asset {0} due to URLError: {1}'.format(url, e.reason)) | ||
| 652 | 632 | except socket.error as e: | |
| 653 | 633 | # Gracefully handle socket errors | |
| 654 | 634 | # TODO: Implement retry logic | |
| 655 | - log_warning('Skipping download of asset {0} due to socker error: {1}'.format(url, e.strerror)) | ||
| 635 | + logger.warning('Skipping download of asset {0} due to socker error: {1}'.format(url, e.strerror)) | ||
| 656 | 636 | ||
| 657 | 637 | ||
| 658 | 638 | def get_authenticated_user(args): | |
@@ -668,15 +648,15 @@ def check_git_lfs_install(): | |||
| 668 | 648 | ||
| 669 | 649 | ||
| 670 | 650 | def retrieve_repositories(args, authenticated_user): | |
| 671 | - log_info('Retrieving repositories') | ||
| 651 | + logger.info('Retrieving repositories') | ||
| 672 | 652 | single_request = False | |
| 673 | 653 | if args.user == authenticated_user['login']: | |
| 674 | 654 | # we must use the /user/repos API to be able to access private repos | |
| 675 | 655 | template = 'https://{0}/user/repos'.format( | |
| 676 | 656 | get_github_api_host(args)) | |
| 677 | 657 | else: | |
| 678 | 658 | if args.private and not args.organization: | |
| 679 | - log_warning('Authenticated user is different from user being backed up, thus private repositories cannot be accessed') | ||
| 659 | + logger.warning('Authenticated user is different from user being backed up, thus private repositories cannot be accessed') | ||
| 680 | 660 | template = 'https://{0}/users/{1}/repos'.format( | |
| 681 | 661 | get_github_api_host(args), | |
| 682 | 662 | args.user) | |
@@ -724,7 +704,7 @@ def retrieve_repositories(args, authenticated_user): | |||
| 724 | 704 | ||
| 725 | 705 | ||
| 726 | 706 | def filter_repositories(args, unfiltered_repositories): | |
| 727 | - log_info('Filtering repositories') | ||
| 707 | + logger.info('Filtering repositories') | ||
| 728 | 708 | ||
| 729 | 709 | repositories = [] | |
| 730 | 710 | for r in unfiltered_repositories: | |
@@ -755,7 +735,7 @@ def filter_repositories(args, unfiltered_repositories): | |||
| 755 | 735 | ||
| 756 | 736 | ||
| 757 | 737 | def backup_repositories(args, output_directory, repositories): | |
| 758 | - log_info('Backing up repositories') | ||
| 738 | + logger.info('Backing up repositories') | ||
| 759 | 739 | repos_template = 'https://{0}/repos'.format(get_github_api_host(args)) | |
| 760 | 740 | ||
| 761 | 741 | if args.incremental: | |
@@ -837,7 +817,7 @@ def backup_issues(args, repo_cwd, repository, repos_template): | |||
| 837 | 817 | if args.skip_existing and has_issues_dir: | |
| 838 | 818 | return | |
| 839 | 819 | ||
| 840 | - log_info('Retrieving {0} issues'.format(repository['full_name'])) | ||
| 820 | + logger.info('Retrieving {0} issues'.format(repository['full_name'])) | ||
| 841 | 821 | issue_cwd = os.path.join(repo_cwd, 'issues') | |
| 842 | 822 | mkdir_p(repo_cwd, issue_cwd) | |
| 843 | 823 | ||
@@ -873,7 +853,7 @@ def backup_issues(args, repo_cwd, repository, repos_template): | |||
| 873 | 853 | issues_skipped_message = ' (skipped {0} pull requests)'.format( | |
| 874 | 854 | issues_skipped) | |
| 875 | 855 | ||
| 876 | - log_info('Saving {0} issues to disk{1}'.format( | ||
| 856 | + logger.info('Saving {0} issues to disk{1}'.format( | ||
| 877 | 857 | len(list(issues.keys())), issues_skipped_message)) | |
| 878 | 858 | comments_template = _issue_template + '/{0}/comments' | |
| 879 | 859 | events_template = _issue_template + '/{0}/events' | |
@@ -895,7 +875,7 @@ def backup_pulls(args, repo_cwd, repository, repos_template): | |||
| 895 | 875 | if args.skip_existing and has_pulls_dir: | |
| 896 | 876 | return | |
| 897 | 877 | ||
| 898 | - log_info('Retrieving {0} pull requests'.format(repository['full_name'])) # noqa | ||
| 878 | + logger.info('Retrieving {0} pull requests'.format(repository['full_name'])) # noqa | ||
| 899 | 879 | pulls_cwd = os.path.join(repo_cwd, 'pulls') | |
| 900 | 880 | mkdir_p(repo_cwd, pulls_cwd) | |
| 901 | 881 | ||
@@ -939,7 +919,7 @@ def backup_pulls(args, repo_cwd, repository, repos_template): | |||
| 939 | 919 | single_request=True | |
| 940 | 920 | )[0] | |
| 941 | 921 | ||
| 942 | - log_info('Saving {0} pull requests to disk'.format( | ||
| 922 | + logger.info('Saving {0} pull requests to disk'.format( | ||
| 943 | 923 | len(list(pulls.keys())))) | |
| 944 | 924 | comments_template = _pulls_template + '/{0}/comments' | |
| 945 | 925 | commits_template = _pulls_template + '/{0}/commits' | |
@@ -961,7 +941,7 @@ def backup_milestones(args, repo_cwd, repository, repos_template): | |||
| 961 | 941 | if args.skip_existing and os.path.isdir(milestone_cwd): | |
| 962 | 942 | return | |
| 963 | 943 | ||
| 964 | - log_info('Retrieving {0} milestones'.format(repository['full_name'])) | ||
| 944 | + logger.info('Retrieving {0} milestones'.format(repository['full_name'])) | ||
| 965 | 945 | mkdir_p(repo_cwd, milestone_cwd) | |
| 966 | 946 | ||
| 967 | 947 | template = '{0}/{1}/milestones'.format(repos_template, | |
@@ -977,7 +957,7 @@ def backup_milestones(args, repo_cwd, repository, repos_template): | |||
| 977 | 957 | for milestone in _milestones: | |
| 978 | 958 | milestones[milestone['number']] = milestone | |
| 979 | 959 | ||
| 980 | - log_info('Saving {0} milestones to disk'.format( | ||
| 960 | + logger.info('Saving {0} milestones to disk'.format( | ||
| 981 | 961 | len(list(milestones.keys())))) | |
| 982 | 962 | for number, milestone in list(milestones.items()): | |
| 983 | 963 | milestone_file = '{0}/{1}.json'.format(milestone_cwd, number) | |
@@ -1000,7 +980,7 @@ def backup_labels(args, repo_cwd, repository, repos_template): | |||
| 1000 | 980 | def backup_hooks(args, repo_cwd, repository, repos_template): | |
| 1001 | 981 | auth = get_auth(args) | |
| 1002 | 982 | if not auth: | |
| 1003 | - log_info("Skipping hooks since no authentication provided") | ||
| 983 | + logger.info("Skipping hooks since no authentication provided") | ||
| 1004 | 984 | return | |
| 1005 | 985 | hook_cwd = os.path.join(repo_cwd, 'hooks') | |
| 1006 | 986 | output_file = '{0}/hooks.json'.format(hook_cwd) | |
@@ -1013,15 +993,15 @@ def backup_hooks(args, repo_cwd, repository, repos_template): | |||
| 1013 | 993 | output_file, | |
| 1014 | 994 | hook_cwd) | |
| 1015 | 995 | except SystemExit: | |
| 1016 | - log_info("Unable to read hooks, skipping") | ||
| 996 | + logger.info("Unable to read hooks, skipping") | ||
| 1017 | 997 | ||
| 1018 | 998 | ||
| 1019 | 999 | def backup_releases(args, repo_cwd, repository, repos_template, include_assets=False): | |
| 1020 | 1000 | repository_fullname = repository['full_name'] | |
| 1021 | 1001 | ||
| 1022 | 1002 | # give release files somewhere to live & log intent | |
| 1023 | 1003 | release_cwd = os.path.join(repo_cwd, 'releases') | |
| 1024 | - log_info('Retrieving {0} releases'.format(repository_fullname)) | ||
| 1004 | + logger.info('Retrieving {0} releases'.format(repository_fullname)) | ||
| 1025 | 1005 | mkdir_p(repo_cwd, release_cwd) | |
| 1026 | 1006 | ||
| 1027 | 1007 | query_args = {} | |
@@ -1030,7 +1010,7 @@ def backup_releases(args, repo_cwd, repository, repos_template, include_assets=F | |||
| 1030 | 1010 | releases = retrieve_data(args, release_template, query_args=query_args) | |
| 1031 | 1011 | ||
| 1032 | 1012 | # for each release, store it | |
| 1033 | - log_info('Saving {0} releases to disk'.format(len(releases))) | ||
| 1013 | + logger.info('Saving {0} releases to disk'.format(len(releases))) | ||
| 1034 | 1014 | for release in releases: | |
| 1035 | 1015 | release_name = release['tag_name'] | |
| 1036 | 1016 | release_name_safe = release_name.replace('/', '__') | |
@@ -1075,12 +1055,12 @@ def fetch_repository(name, | |||
| 1075 | 1055 | stderr=FNULL, | |
| 1076 | 1056 | shell=True) | |
| 1077 | 1057 | if initialized == 128: | |
| 1078 | - log_info("Skipping {0} ({1}) since it's not initialized".format( | ||
| 1058 | + logger.info("Skipping {0} ({1}) since it's not initialized".format( | ||
| 1079 | 1059 | name, masked_remote_url)) | |
| 1080 | 1060 | return | |
| 1081 | 1061 | ||
| 1082 | 1062 | if clone_exists: | |
| 1083 | - log_info('Updating {0} in {1}'.format(name, local_dir)) | ||
| 1063 | + logger.info('Updating {0} in {1}'.format(name, local_dir)) | ||
| 1084 | 1064 | ||
| 1085 | 1065 | remotes = subprocess.check_output(['git', 'remote', 'show'], | |
| 1086 | 1066 | cwd=local_dir) | |
@@ -1101,7 +1081,7 @@ def fetch_repository(name, | |||
| 1101 | 1081 | git_command = ['git', 'fetch', '--all', '--force', '--tags', '--prune'] | |
| 1102 | 1082 | logging_subprocess(git_command, None, cwd=local_dir) | |
| 1103 | 1083 | else: | |
| 1104 | - log_info('Cloning {0} repository from {1} to {2}'.format( | ||
| 1084 | + logger.info('Cloning {0} repository from {1} to {2}'.format( | ||
| 1105 | 1085 | name, | |
| 1106 | 1086 | masked_remote_url, | |
| 1107 | 1087 | local_dir)) | |
@@ -1161,11 +1141,11 @@ def backup_account(args, output_directory): | |||
| 1161 | 1141 | def _backup_data(args, name, template, output_file, output_directory): | |
| 1162 | 1142 | skip_existing = args.skip_existing | |
| 1163 | 1143 | if not skip_existing or not os.path.exists(output_file): | |
| 1164 | - log_info('Retrieving {0} {1}'.format(args.user, name)) | ||
| 1144 | + logger.info('Retrieving {0} {1}'.format(args.user, name)) | ||
| 1165 | 1145 | mkdir_p(output_directory) | |
| 1166 | 1146 | data = retrieve_data(args, template) | |
| 1167 | 1147 | ||
| 1168 | - log_info('Writing {0} {1} to disk'.format(len(data), name)) | ||
| 1148 | + logger.info('Writing {0} {1} to disk'.format(len(data), name)) | ||
| 1169 | 1149 | with codecs.open(output_file, 'w', encoding='utf-8') as f: | |
| 1170 | 1150 | json_dump(data, f) | |
| 1171 | 1151 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments