| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # TODO(hinoka): Use logging. | 6 # TODO(hinoka): Use logging. |
| 7 | 7 |
| 8 import cStringIO | 8 import cStringIO |
| 9 import codecs | 9 import codecs |
| 10 import collections | 10 import collections |
| (...skipping 604 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 615 if not ok: | 615 if not ok: |
| 616 # Get off of the temporary branch since it can't be deleted otherwise. | 616 # Get off of the temporary branch since it can't be deleted otherwise. |
| 617 git('checkout', base_rev, cwd=root) | 617 git('checkout', base_rev, cwd=root) |
| 618 git('branch', '-D', temp_branch_name, cwd=root) | 618 git('branch', '-D', temp_branch_name, cwd=root) |
| 619 | 619 |
| 620 if gerrit_reset: | 620 if gerrit_reset: |
| 621 git('reset', '--soft', base_rev, cwd=root) | 621 git('reset', '--soft', base_rev, cwd=root) |
| 622 except SubprocessFailed as e: | 622 except SubprocessFailed as e: |
| 623 raise PatchFailed(e.message, e.code, e.output) | 623 raise PatchFailed(e.message, e.code, e.output) |
| 624 | 624 |
| 625 def check_flag(flag_file): | |
| 626 """Returns True if the flag file is present.""" | |
| 627 return os.path.isfile(flag_file) | |
| 628 | |
| 629 | |
| 630 def delete_flag(flag_file): | |
| 631 """Remove bot update flag.""" | |
| 632 if os.path.isfile(flag_file): | |
| 633 os.remove(flag_file) | |
| 634 | |
| 635 | |
| 636 def emit_flag(flag_file): | |
| 637 """Deposit a bot update flag on the system to tell gclient not to run.""" | |
| 638 print 'Emitting flag file at %s' % flag_file | |
| 639 with open(flag_file, 'wb') as f: | |
| 640 f.write('Success!') | |
| 641 | |
| 642 | 625 |
| 643 def get_commit_position(git_path, revision='HEAD'): | 626 def get_commit_position(git_path, revision='HEAD'): |
| 644 """Dumps the 'git' log for a specific revision and parses out the commit | 627 """Dumps the 'git' log for a specific revision and parses out the commit |
| 645 position. | 628 position. |
| 646 | 629 |
| 647 If a commit position metadata key is found, its value will be returned. | 630 If a commit position metadata key is found, its value will be returned. |
| 648 """ | 631 """ |
| 649 # TODO(iannucci): Use git-footers for this. | 632 # TODO(iannucci): Use git-footers for this. |
| 650 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) | 633 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) |
| 651 footer_map = get_commit_message_footer_map(git_log) | 634 footer_map = get_commit_message_footer_map(git_log) |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 843 default='codereview.chromium.org', | 826 default='codereview.chromium.org', |
| 844 help='Rietveld server.') | 827 help='Rietveld server.') |
| 845 parse.add_option('--gerrit_repo', | 828 parse.add_option('--gerrit_repo', |
| 846 help='Gerrit repository to pull the ref from.') | 829 help='Gerrit repository to pull the ref from.') |
| 847 parse.add_option('--gerrit_ref', help='Gerrit ref to apply.') | 830 parse.add_option('--gerrit_ref', help='Gerrit ref to apply.') |
| 848 parse.add_option('--gerrit_no_rebase_patch_ref', action='store_true', | 831 parse.add_option('--gerrit_no_rebase_patch_ref', action='store_true', |
| 849 help='Bypass rebase of Gerrit patch ref after checkout.') | 832 help='Bypass rebase of Gerrit patch ref after checkout.') |
| 850 parse.add_option('--gerrit_no_reset', action='store_true', | 833 parse.add_option('--gerrit_no_reset', action='store_true', |
| 851 help='Bypass calling reset after applying a gerrit ref.') | 834 help='Bypass calling reset after applying a gerrit ref.') |
| 852 parse.add_option('--specs', help='Gcilent spec.') | 835 parse.add_option('--specs', help='Gcilent spec.') |
| 853 parse.add_option('-f', '--force', action='store_true', | |
| 854 help='Bypass check to see if we want to be run. ' | |
| 855 'Should ONLY be used locally or by smart recipes.') | |
| 856 parse.add_option('--revision_mapping', | |
| 857 help='{"path/to/repo/": "property_name"}') | |
| 858 parse.add_option('--revision_mapping_file', | 836 parse.add_option('--revision_mapping_file', |
| 859 help=('Same as revision_mapping, except its a path to a json' | 837 help=('Path to a json file of the form ' |
| 860 ' file containing that format.')) | 838 '{"path/to/repo/": "property_name"}')) |
| 861 parse.add_option('--revision', action='append', default=[], | 839 parse.add_option('--revision', action='append', default=[], |
| 862 help='Revision to check out. Can be any form of git ref. ' | 840 help='Revision to check out. Can be any form of git ref. ' |
| 863 'Can prepend root@<rev> to specify which repository, ' | 841 'Can prepend root@<rev> to specify which repository, ' |
| 864 'where root is either a filesystem path or git https ' | 842 'where root is either a filesystem path or git https ' |
| 865 'url. To specify Tip of Tree, set rev to HEAD. ') | 843 'url. To specify Tip of Tree, set rev to HEAD. ') |
| 866 parse.add_option('--output_manifest', action='store_true', | 844 parse.add_option('--output_manifest', action='store_true', |
| 867 help=('Add manifest json to the json output.')) | 845 help=('Add manifest json to the json output.')) |
| 868 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], | |
| 869 help='Hostname of the current machine, ' | |
| 870 'used for determining whether or not to activate.') | |
| 871 parse.add_option('--build_dir', default=os.getcwd()) | |
| 872 parse.add_option('--flag_file', default=path.join(os.getcwd(), | |
| 873 'update.flag')) | |
| 874 parse.add_option('--shallow', action='store_true', | |
| 875 help='Use shallow clones for cache repositories.') | |
| 876 parse.add_option('--clobber', action='store_true', | 846 parse.add_option('--clobber', action='store_true', |
| 877 help='Delete checkout first, always') | 847 help='Delete checkout first, always') |
| 878 parse.add_option('--bot_update_clobber', action='store_true', dest='clobber', | 848 parse.add_option('--output_json', |
| 879 help='(synonym for --clobber)') | |
| 880 parse.add_option('-o', '--output_json', | |
| 881 help='Output JSON information into a specified file') | 849 help='Output JSON information into a specified file') |
| 882 parse.add_option('--no_shallow', action='store_true', | 850 parse.add_option('--no_shallow', action='store_true', |
| 883 help='Bypass disk detection and never shallow clone. ' | 851 help='Bypass disk detection and never shallow clone. ' |
| 884 'Does not override the --shallow flag') | 852 'Does not override the --shallow flag') |
| 885 parse.add_option('--refs', action='append', | 853 parse.add_option('--refs', action='append', |
| 886 help='Also fetch this refspec for the main solution(s). ' | 854 help='Also fetch this refspec for the main solution(s). ' |
| 887 'Eg. +refs/branch-heads/*') | 855 'Eg. +refs/branch-heads/*') |
| 888 parse.add_option('--with_branch_heads', action='store_true', | 856 parse.add_option('--with_branch_heads', action='store_true', |
| 889 help='Always pass --with_branch_heads to gclient. This ' | 857 help='Always pass --with_branch_heads to gclient. This ' |
| 890 'does the same thing as --refs +refs/branch-heads/*') | 858 'does the same thing as --refs +refs/branch-heads/*') |
| 891 parse.add_option('--git-cache-dir', help='Path to git cache directory.') | 859 parse.add_option('--git-cache-dir', help='Path to git cache directory.') |
| 892 | 860 |
| 893 | 861 |
| 894 options, args = parse.parse_args() | 862 options, args = parse.parse_args() |
| 895 | 863 |
| 896 if not options.git_cache_dir: | 864 if not options.git_cache_dir: |
| 897 parse.error('--git-cache-dir is required') | 865 parse.error('--git-cache-dir is required') |
| 898 | 866 |
| 899 if not options.refs: | 867 if not options.refs: |
| 900 options.refs = [] | 868 options.refs = [] |
| 901 | 869 |
| 902 if options.with_branch_heads: | 870 if options.with_branch_heads: |
| 903 options.refs.append(BRANCH_HEADS_REFSPEC) | 871 options.refs.append(BRANCH_HEADS_REFSPEC) |
| 904 del options.with_branch_heads | 872 del options.with_branch_heads |
| 905 | 873 |
| 906 try: | 874 try: |
| 907 if options.revision_mapping_file: | 875 if not options.revision_mapping_file: |
| 908 if options.revision_mapping: | 876 parse.error('--revision_mapping_file is required') |
| 909 print ('WARNING: Ignoring --revision_mapping: --revision_mapping_file ' | 877 |
| 910 'was set at the same time as --revision_mapping?') | 878 with open(options.revision_mapping_file, 'r') as f: |
| 911 with open(options.revision_mapping_file, 'r') as f: | 879 options.revision_mapping = json.load(f) |
| 912 options.revision_mapping = json.load(f) | |
| 913 elif options.revision_mapping: | |
| 914 options.revision_mapping = json.loads(options.revision_mapping) | |
| 915 except Exception as e: | 880 except Exception as e: |
| 916 print ( | 881 print ( |
| 917 'WARNING: Caught execption while parsing revision_mapping*: %s' | 882 'WARNING: Caught execption while parsing revision_mapping*: %s' |
| 918 % (str(e),) | 883 % (str(e),) |
| 919 ) | 884 ) |
| 920 | 885 |
| 921 # Because we print CACHE_DIR out into a .gclient file, and then later run | 886 # Because we print CACHE_DIR out into a .gclient file, and then later run |
| 922 # eval() on it, backslashes need to be escaped, otherwise "E:\b\build" gets | 887 # eval() on it, backslashes need to be escaped, otherwise "E:\b\build" gets |
| 923 # parsed as "E:[\x08][\x08]uild". | 888 # parsed as "E:[\x08][\x08]uild". |
| 924 if sys.platform.startswith('win'): | 889 if sys.platform.startswith('win'): |
| 925 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') | 890 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') |
| 926 | 891 |
| 927 return options, args | 892 return options, args |
| 928 | 893 |
| 929 | 894 |
| 930 def prepare(options, git_slns, active): | 895 def prepare(options, git_slns, active): |
| 931 """Prepares the target folder before we checkout.""" | 896 """Prepares the target folder before we checkout.""" |
| 932 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] | 897 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] |
| 933 # If we're active now, but the flag file doesn't exist (we weren't active | 898 if options.clobber: |
| 934 # last run) or vice versa, blow away all checkouts. | |
| 935 if options.clobber or (bool(active) != bool(check_flag(options.flag_file))): | |
| 936 ensure_no_checkout(dir_names) | 899 ensure_no_checkout(dir_names) |
| 937 if options.output_json: | 900 # Make sure we tell recipes that we didn't run if the script exits here. |
| 938 # Make sure we tell recipes that we didn't run if the script exits here. | 901 emit_json(options.output_json, did_run=active) |
| 939 emit_json(options.output_json, did_run=active) | |
| 940 emit_flag(options.flag_file) | |
| 941 | 902 |
| 942 # Do a shallow checkout if the disk is less than 100GB. | 903 # Do a shallow checkout if the disk is less than 100GB. |
| 943 total_disk_space, free_disk_space = get_total_disk_space() | 904 total_disk_space, free_disk_space = get_total_disk_space() |
| 944 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) | 905 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) |
| 945 used_disk_space_gb = int((total_disk_space - free_disk_space) | 906 used_disk_space_gb = int((total_disk_space - free_disk_space) |
| 946 / (1024 * 1024 * 1024)) | 907 / (1024 * 1024 * 1024)) |
| 947 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) | 908 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) |
| 948 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, | 909 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, |
| 949 total_disk_space_gb, | 910 total_disk_space_gb, |
| 950 percent_used) | 911 percent_used) |
| 951 if not options.output_json: | 912 if not options.output_json: |
| 952 print '@@@STEP_TEXT@%s@@@' % step_text | 913 print '@@@STEP_TEXT@%s@@@' % step_text |
| 953 if not options.shallow: | 914 shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD |
| 954 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD | 915 and not options.no_shallow) |
| 955 and not options.no_shallow) | |
| 956 | 916 |
| 957 # The first solution is where the primary DEPS file resides. | 917 # The first solution is where the primary DEPS file resides. |
| 958 first_sln = dir_names[0] | 918 first_sln = dir_names[0] |
| 959 | 919 |
| 960 # Split all the revision specifications into a nice dict. | 920 # Split all the revision specifications into a nice dict. |
| 961 print 'Revisions: %s' % options.revision | 921 print 'Revisions: %s' % options.revision |
| 962 revisions = parse_revisions(options.revision, first_sln) | 922 revisions = parse_revisions(options.revision, first_sln) |
| 963 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) | 923 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) |
| 964 return revisions, step_text | 924 return revisions, step_text, shallow |
| 965 | 925 |
| 966 | 926 |
| 967 def checkout(options, git_slns, specs, revisions, step_text): | 927 def checkout(options, git_slns, specs, revisions, step_text, shallow): |
| 968 first_sln = git_slns[0]['name'] | 928 first_sln = git_slns[0]['name'] |
| 969 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] | 929 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] |
| 970 try: | 930 try: |
| 971 # Outer try is for catching patch failures and exiting gracefully. | 931 # Outer try is for catching patch failures and exiting gracefully. |
| 972 # Inner try is for catching gclient failures and retrying gracefully. | 932 # Inner try is for catching gclient failures and retrying gracefully. |
| 973 try: | 933 try: |
| 974 checkout_parameters = dict( | 934 checkout_parameters = dict( |
| 975 # First, pass in the base of what we want to check out. | 935 # First, pass in the base of what we want to check out. |
| 976 solutions=git_slns, | 936 solutions=git_slns, |
| 977 revisions=revisions, | 937 revisions=revisions, |
| 978 first_sln=first_sln, | 938 first_sln=first_sln, |
| 979 | 939 |
| 980 # Also, target os variables for gclient. | 940 # Also, target os variables for gclient. |
| 981 target_os=specs.get('target_os', []), | 941 target_os=specs.get('target_os', []), |
| 982 target_os_only=specs.get('target_os_only', False), | 942 target_os_only=specs.get('target_os_only', False), |
| 983 | 943 |
| 984 # Then, pass in information about how to patch. | 944 # Then, pass in information about how to patch. |
| 985 patch_root=options.patch_root, | 945 patch_root=options.patch_root, |
| 986 issue=options.issue, | 946 issue=options.issue, |
| 987 patchset=options.patchset, | 947 patchset=options.patchset, |
| 988 rietveld_server=options.rietveld_server, | 948 rietveld_server=options.rietveld_server, |
| 989 gerrit_repo=options.gerrit_repo, | 949 gerrit_repo=options.gerrit_repo, |
| 990 gerrit_ref=options.gerrit_ref, | 950 gerrit_ref=options.gerrit_ref, |
| 991 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref, | 951 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref, |
| 992 revision_mapping=options.revision_mapping, | 952 revision_mapping=options.revision_mapping, |
| 993 apply_issue_email_file=options.apply_issue_email_file, | 953 apply_issue_email_file=options.apply_issue_email_file, |
| 994 apply_issue_key_file=options.apply_issue_key_file, | 954 apply_issue_key_file=options.apply_issue_key_file, |
| 995 | 955 |
| 996 # Finally, extra configurations such as shallowness of the clone. | 956 # Finally, extra configurations such as shallowness of the clone. |
| 997 shallow=options.shallow, | 957 shallow=shallow, |
| 998 refs=options.refs, | 958 refs=options.refs, |
| 999 git_cache_dir=options.git_cache_dir, | 959 git_cache_dir=options.git_cache_dir, |
| 1000 gerrit_reset=not options.gerrit_no_reset) | 960 gerrit_reset=not options.gerrit_no_reset) |
| 1001 gclient_output = ensure_checkout(**checkout_parameters) | 961 gclient_output = ensure_checkout(**checkout_parameters) |
| 1002 except GclientSyncFailed: | 962 except GclientSyncFailed: |
| 1003 print 'We failed gclient sync, lets delete the checkout and retry.' | 963 print 'We failed gclient sync, lets delete the checkout and retry.' |
| 1004 ensure_no_checkout(dir_names) | 964 ensure_no_checkout(dir_names) |
| 1005 gclient_output = ensure_checkout(**checkout_parameters) | 965 gclient_output = ensure_checkout(**checkout_parameters) |
| 1006 except PatchFailed as e: | 966 except PatchFailed as e: |
| 1007 if options.output_json: | 967 if options.output_json: |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1066 'DEPOT_TOOLS_DIR': DEPOT_TOOLS_DIR, | 1026 'DEPOT_TOOLS_DIR': DEPOT_TOOLS_DIR, |
| 1067 } | 1027 } |
| 1068 for k, v in sorted(debug_params.iteritems()): | 1028 for k, v in sorted(debug_params.iteritems()): |
| 1069 print "%s: %r" % (k, v) | 1029 print "%s: %r" % (k, v) |
| 1070 | 1030 |
| 1071 | 1031 |
| 1072 def main(): | 1032 def main(): |
| 1073 # Get inputs. | 1033 # Get inputs. |
| 1074 options, _ = parse_args() | 1034 options, _ = parse_args() |
| 1075 | 1035 |
| 1076 # Always run. This option will be removed in a later CL, but for now make sure | |
| 1077 # that bot_update is ALWAYS set to run, no matter what. | |
| 1078 options.force = True | |
| 1079 | |
| 1080 # Check if this script should activate or not. | 1036 # Check if this script should activate or not. |
| 1081 active = True | 1037 active = True |
| 1082 | 1038 |
| 1083 # Print a helpful message to tell developers whats going on with this step. | 1039 # Print a helpful message to tell developers whats going on with this step. |
| 1084 print_debug_info() | 1040 print_debug_info() |
| 1085 | 1041 |
| 1086 # Parse, munipulate, and print the gclient solutions. | 1042 # Parse, munipulate, and print the gclient solutions. |
| 1087 specs = {} | 1043 specs = {} |
| 1088 exec(options.specs, specs) | 1044 exec(options.specs, specs) |
| 1089 orig_solutions = specs.get('solutions', []) | 1045 orig_solutions = specs.get('solutions', []) |
| 1090 git_slns = modify_solutions(orig_solutions) | 1046 git_slns = modify_solutions(orig_solutions) |
| 1091 | 1047 |
| 1092 solutions_printer(git_slns) | 1048 solutions_printer(git_slns) |
| 1093 | 1049 |
| 1094 try: | 1050 try: |
| 1095 # Dun dun dun, the main part of bot_update. | 1051 # Dun dun dun, the main part of bot_update. |
| 1096 revisions, step_text = prepare(options, git_slns, active) | 1052 revisions, step_text, shallow = prepare(options, git_slns, active) |
| 1097 checkout(options, git_slns, specs, revisions, step_text) | 1053 checkout(options, git_slns, specs, revisions, step_text, shallow) |
| 1098 | 1054 |
| 1099 except PatchFailed as e: | 1055 except PatchFailed as e: |
| 1100 emit_flag(options.flag_file) | |
| 1101 # Return a specific non-zero exit code for patch failure (because it is | 1056 # Return a specific non-zero exit code for patch failure (because it is |
| 1102 # a failure), but make it different than other failures to distinguish | 1057 # a failure), but make it different than other failures to distinguish |
| 1103 # between infra failures (independent from patch author), and patch | 1058 # between infra failures (independent from patch author), and patch |
| 1104 # failures (that patch author can fix). However, PatchFailure due to | 1059 # failures (that patch author can fix). However, PatchFailure due to |
| 1105 # download patch failure is still an infra problem. | 1060 # download patch failure is still an infra problem. |
| 1106 if e.code == 3: | 1061 if e.code == 3: |
| 1107 # Patch download problem. | 1062 # Patch download problem. |
| 1108 return 87 | 1063 return 87 |
| 1109 # Genuine patch problem. | 1064 # Genuine patch problem. |
| 1110 return 88 | 1065 return 88 |
| 1111 except Exception: | |
| 1112 # Unexpected failure. | |
| 1113 emit_flag(options.flag_file) | |
| 1114 raise | |
| 1115 else: | |
| 1116 emit_flag(options.flag_file) | |
| 1117 | 1066 |
| 1118 | 1067 |
| 1119 if __name__ == '__main__': | 1068 if __name__ == '__main__': |
| 1120 sys.exit(main()) | 1069 sys.exit(main()) |
| OLD | NEW |