| """Landmine step for clobbering output directories when needed. |
| |
| This step checks for a landmine file in continuous-tests/cq/landmines, and |
| clobbers all the output directories if it finds a new landmine. |
| |
| For more information on using landmines, checkout out the documentation at |
| continous-tests/cq/landmines/README.md |
| """ |
| |
| from __future__ import absolute_import |
| import logging |
| import os |
| import re |
| |
| from helpers import git_utils |
| from slave.step import shell_step |
| |
| LANDMINE_PATTERN = r'landmine\.(?P<landmine_number>\d+)' |
| |
| |
| class LandmineStep(shell_step.ShellStep): |
| """Landmine step for clobbering output directories when needed.""" |
| |
| def __init__(self, eureka_root, **kwargs): |
| """Creates a LandmineStep instance.""" |
| shell_step.ShellStep.__init__(self, |
| name='landmine', |
| halt_on_failure=True, |
| **kwargs) |
| self._landmine_path = eureka_root / '.landmines' |
| |
| def get_landmine_dir(self): |
| """Return the directory where new landmine files get checked in.""" |
| continuous_tests = self.get_project_path('continuous-tests') |
| return os.path.join(continuous_tests, 'cq', 'landmines') |
| |
| def get_landmine(self): |
| """Returns the name of landmine file with the highest number (or None).""" |
| landmine = None |
| max_landmine_number = float('-inf') |
| |
| landmine_dir = self.get_landmine_dir() |
| if not os.path.isdir(landmine_dir): |
| return landmine |
| |
| for filename in os.listdir(landmine_dir): |
| match = re.search(LANDMINE_PATTERN, filename) |
| if match: |
| logging.info('Found landmine file: %s', filename) |
| landmine_number = int(match.group('landmine_number')) |
| if landmine_number > max_landmine_number: |
| landmine, max_landmine_number = filename, landmine_number |
| logging.info('Highest landmine: %s', landmine) |
| return landmine |
| |
| def should_clobber(self, landmine): |
| """Returns True if |landmine| is new, indicating a clobber should happen.""" |
| return not (self._landmine_path / landmine).exists() |
| |
| def should_mark_landmine_used(self, landmine): |
| """A landmine should only be marked used once it is committed. |
| |
| If a landmine is in the patches running through CQ, then do not mark |
| the landmine as used, so that if a builder runs another CL between running |
| the landmine CL and the landmine actually landing, the landmine can run |
| again. |
| """ |
| all_patches = [{'project': self.patch_project}] + self.depends_on_list |
| changed_files = git_utils.get_changed_files_in_all_patches( |
| self, all_patches, self.get_project_path_lookup_table()) |
| return all(landmine not in f for f in changed_files) |
| |
| def get_commands(self): |
| commands = [] |
| landmine = self.get_landmine() |
| if landmine and self.should_clobber(landmine): |
| commands.append(self.ShellCommand(['make', 'clean'], cwd=os.getcwd())) |
| commands.append(self.ShellCommand( |
| ['mkdir', '-p', str(self._landmine_path)], |
| cwd=os.getcwd())) |
| |
| if self.should_mark_landmine_used(landmine): |
| commands.append(self.ShellCommand( |
| ['touch', str(self._landmine_path / landmine)], |
| cwd=os.getcwd())) |
| |
| return commands |