mozlint.py (3357B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 import logging 6 from pathlib import Path 7 from typing import Union 8 9 from mozlint.parser import Parser 10 from mozlint.pathutils import filterpaths 11 from taskgraph.optimize.base import OptimizationStrategy 12 from taskgraph.util.path import match as match_path 13 14 from gecko_taskgraph import GECKO 15 16 logger = logging.getLogger(__name__) 17 18 19 class TGMozlintParser(Parser): 20 """ 21 Mozlint Parser that skips validation. This is needed because decision 22 tasks use sparse clones and the files themselves are not present. 23 """ 24 25 def _validate(self, linter): 26 pass 27 28 29 class SkipUnlessMozlint(OptimizationStrategy): 30 """ 31 Optimization strategy for mozlint tests. 32 33 Uses the mozlint YAML file for each test to determine whether or not a test 34 should run. 35 36 Using: 37 - The optimization relies on having `files_changed` set in the decision task 38 parameters. 39 - Register with register_strategy. The argument is the path to MozLint YAML 40 configuration files ("/tools/lint" for mozilla-central): 41 - In source-test/mozlint.yml, set the optimization strategy for each job by filename: 42 - For Mozlint jobs that run multiple linters at once use a list of filenames: 43 """ 44 45 def __init__(self, linters_path: str): 46 self.mozlint_parser = TGMozlintParser(GECKO) 47 self.linters_path = Path(GECKO) / linters_path 48 49 def should_remove_task( 50 self, task, params, mozlint_confs: Union[str, list[str]] 51 ) -> bool: 52 include = [] 53 exclude = [] 54 extensions = [] 55 exclude_extensions = [] 56 support_files = ["python/mozlint/**", "tools/lint/**"] 57 58 files_changed = params["files_changed"] 59 60 if isinstance(mozlint_confs, str): 61 mozlint_confs = [mozlint_confs] 62 63 for mozlint_conf in mozlint_confs: 64 mozlint_yaml = str(self.linters_path / mozlint_conf) 65 logger.info(f"Loading file patterns for {task.label} from {mozlint_yaml}.") 66 linter_config = self.mozlint_parser(mozlint_yaml) 67 68 for config in linter_config: 69 include += config.get("include", []) 70 exclude += config.get("exclude", []) 71 extensions += [e.strip(".") for e in config.get("extensions", [])] 72 exclude_extensions += [ 73 e.strip(".") for e in config.get("exclude_extensions", []) 74 ] 75 support_files += config.get("support-files", []) 76 77 # Support files may not be part of "include" patterns, so check first 78 # Do not remove (return False) if any changed 79 for pattern in set(support_files): 80 for path in files_changed: 81 if match_path(path, pattern): 82 return False 83 84 to_lint, to_exclude = filterpaths( 85 GECKO, 86 list(files_changed), 87 include=include, 88 exclude=exclude, 89 extensions=extensions, 90 exclude_extensions=exclude_extensions, 91 ) 92 93 # to_lint should be an empty list if there is nothing to check 94 if not to_lint: 95 return True 96 return False