diff --git a/runbot/models/build.py b/runbot/models/build.py index bbde5a2a3..6f5a2e5cb 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -44,6 +44,30 @@ USERUID = os.getuid() USERNAME = getpass.getuser() + +def force_rmtree(top_dir): + """Rewrite of rmtree that tries to handle permission errors (e.g.: when a directory that the user own is u-w) + :param top_dir: diretory to remove + :type top_dir: str or Path + """ + top = Path(top_dir) + for root, dirs, files in top.walk(top_down=False): + for name in files: + f = root / name + try: + f.unlink() + except PermissionError: + perm = f.stat().st_mode | 0o00600 + f.chmod(perm) + pperm = f.parent.stat().st_mode | 0o00700 + f.parent.chmod(pperm) + f.unlink() + for name in dirs: + d = root / name + d.rmdir() + top.rmdir() + + def make_selection(array): return [(elem, elem.replace('_', ' ').capitalize()) if isinstance(elem, str) else elem for elem in array] @@ -712,11 +736,17 @@ def filter_ids(dest_list, label): gcstamp = build_dir / '.gcstamp' for bdir_file in build_dir.iterdir(): if bdir_file.is_dir() and bdir_file.name not in ('logs', 'tests'): - shutil.rmtree(bdir_file) + try: + force_rmtree(bdir_file) + except Exception: + _logger.exception('Failed to remove %s', bdir_file) elif bdir_file.name == 'logs': for log_file_path in bdir_file.iterdir(): if log_file_path.is_dir(): - shutil.rmtree(log_file_path) + try: + force_rmtree(log_file_path) + except Exception: + _logger.exception('Failed to remove %s', log_file_path) elif log_file_path.name in ('run.txt', 'wake_up.txt'): log_file_path.unlink() elif log_file_path.name.endswith('.zip'):