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 |