PRESUBMIT.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # Copyright 2018 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. """Enforces luci-milo.cfg consistency.
  5. See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
  6. for more details on the presubmit API built into depot_tools.
  7. """
  8. PRESUBMIT_VERSION = '2.0.0'
  9. USE_PYTHON3 = True
  10. _IGNORE_FREEZE_FOOTER = 'Ignore-Freeze'
  11. # The time module's handling of timezones is abysmal, so the boundaries are
  12. # precomputed in UNIX time
  13. _FREEZE_START = 1639641600 # 2021/12/16 00:00 -0800
  14. _FREEZE_END = 1641196800 # 2022/01/03 00:00 -0800
  15. def CheckFreeze(input_api, output_api):
  16. if _FREEZE_START <= input_api.time.time() < _FREEZE_END:
  17. footers = input_api.change.GitFootersFromDescription()
  18. if _IGNORE_FREEZE_FOOTER not in footers:
  19. def convert(t):
  20. ts = input_api.time.localtime(t)
  21. return input_api.time.strftime('%Y/%m/%d %H:%M %z', ts)
  22. return [
  23. output_api.PresubmitError(
  24. 'There is a prod freeze in effect from {} until {},'
  25. ' files in //infra/config cannot be modified'.format(
  26. convert(_FREEZE_START), convert(_FREEZE_END)))
  27. ]
  28. return []
  29. def CheckTests(input_api, output_api):
  30. glob = input_api.os_path.join(input_api.PresubmitLocalPath(), '*_test.py')
  31. tests = input_api.canned_checks.GetUnitTests(input_api,
  32. output_api,
  33. input_api.glob(glob),
  34. run_on_python2=False,
  35. run_on_python3=True,
  36. skip_shebang_check=True)
  37. return input_api.RunTests(tests)
  38. def CheckLintLuciMilo(input_api, output_api):
  39. if ('infra/config/generated/luci/luci-milo.cfg' in input_api.LocalPaths()
  40. or 'infra/config/lint-luci-milo.py' in input_api.LocalPaths()):
  41. return input_api.RunTests([
  42. input_api.Command(
  43. name='lint-luci-milo',
  44. cmd=[input_api.python3_executable, 'lint-luci-milo.py'],
  45. kwargs={},
  46. message=output_api.PresubmitError),
  47. ])
  48. return []
  49. def CheckTestingBuildbot(input_api, output_api):
  50. if ('infra/config/generated/luci/luci-milo.cfg' in input_api.LocalPaths() or
  51. 'infra/config/generated/luci/luci-milo-dev.cfg' in input_api.LocalPaths()
  52. ):
  53. return input_api.RunTests([
  54. input_api.Command(name='testing/buildbot config checks',
  55. cmd=[
  56. input_api.python3_executable,
  57. input_api.os_path.join(
  58. '..',
  59. '..',
  60. 'testing',
  61. 'buildbot',
  62. 'generate_buildbot_json.py',
  63. ), '--check'
  64. ],
  65. kwargs={},
  66. message=output_api.PresubmitError),
  67. ])
  68. return []
  69. def CheckLucicfgGenOutputMain(input_api, output_api):
  70. return input_api.RunTests(input_api.canned_checks.CheckLucicfgGenOutput(
  71. input_api, output_api, 'main.star'))
  72. def CheckLucicfgGenOutputDev(input_api, output_api):
  73. return input_api.RunTests(input_api.canned_checks.CheckLucicfgGenOutput(
  74. input_api, output_api, 'dev.star'))
  75. def CheckChangedLUCIConfigs(input_api, output_api):
  76. return input_api.canned_checks.CheckChangedLUCIConfigs(
  77. input_api, output_api)
  78. # Footer indicating a CL that is trying to address an outage by some mechanism
  79. # other than those in infra/config/outages
  80. _OUTAGE_ACTION_FOOTER = 'Infra-Config-Outage-Action'
  81. # Footer acknowledging that an outages configuration is in effect when making an
  82. # unrelated change
  83. _IGNORE_OUTAGE_FOOTER = 'Infra-Config-Ignore-Outage'
  84. def CheckOutagesConfigOnCommit(input_api, output_api):
  85. outages_pyl = input_api.os_path.join(
  86. input_api.PresubmitLocalPath(), 'generated/outages.pyl')
  87. with open(outages_pyl) as f:
  88. outages_config = input_api.ast.literal_eval(f.read())
  89. if not outages_config:
  90. footers = input_api.change.GitFootersFromDescription()
  91. return [
  92. output_api.PresubmitError(
  93. 'There is no outages configuration in effect, '
  94. 'please remove the {} footer from your CL description.'
  95. .format(footer))
  96. for footer in (_OUTAGE_ACTION_FOOTER, _IGNORE_OUTAGE_FOOTER)
  97. if footer in footers
  98. ]
  99. # Any of the config files under infra/config/outages
  100. outages_config_files = set()
  101. # Any of the config files under infra/config/generated
  102. generated_config_files = set()
  103. # Any config files that are not under infra/config/outages or
  104. # infra/config/generated
  105. config_files = set()
  106. for p in input_api.LocalPaths():
  107. if p in ('README.md', 'OWNERS'):
  108. continue
  109. if p.startswith('infra/config/outages/'):
  110. outages_config_files.add(p)
  111. continue
  112. if p.startswith('infra/config/generated/'):
  113. generated_config_files.add(p)
  114. continue
  115. config_files.add(p)
  116. # If the only changes to non-generated config fies were the outages files,
  117. # assume the change was addressing an outage and that no additional mechanism
  118. # needs to be added
  119. if outages_config_files and not config_files:
  120. # REVIEWER: Should we prevent the footers from being here in this case?
  121. return []
  122. # If any non-generated, non-outages files were modified or if the generated
  123. # config files were modified without any config files being modified (lucicfg
  124. # change, etc.) then make sure the user knows that when the outages
  125. # configuration is disabled, the generated configuration may change
  126. if config_files or generated_config_files:
  127. footers = input_api.change.GitFootersFromDescription()
  128. has_action_footer = _OUTAGE_ACTION_FOOTER in footers
  129. has_ignore_footer = _IGNORE_OUTAGE_FOOTER in footers
  130. if has_action_footer and has_ignore_footer:
  131. return [
  132. output_api.PresubmitError(
  133. 'Only one of {} or {} should be present in your CL description'
  134. .format(_OUTAGE_ACTION_FOOTER, _IGNORE_OUTAGE_FOOTER)),
  135. ]
  136. if not has_action_footer and not has_ignore_footer:
  137. outages_config_lines = ['{}: {}'.format(k, v)
  138. for k, v in sorted(outages_config.items())]
  139. return [
  140. output_api.PresubmitError('\n'.join([
  141. 'The following outages configuration is in effect:\n {}'.format(
  142. '\n '.join(outages_config_lines)),
  143. ('The effect of your change may not be visible '
  144. 'in the generated configuration.'),
  145. ('If your change is addressing the outage, '
  146. 'please add the footer {} with a link for the outage.'
  147. ).format(_OUTAGE_ACTION_FOOTER),
  148. ('If your change is not addressing the outage '
  149. 'but you still wish to land it, please add the footer '
  150. '{} with a reason.').format(_IGNORE_OUTAGE_FOOTER),
  151. ('For more information on outages configuration, '
  152. 'see https://chromium.googlesource.com/chromium/src/+/HEAD/infra/config/outages'
  153. ),
  154. ])),
  155. ]
  156. return []