compare-mozconfigs.py (5711B)
1 #!/usr/bin/python 2 # This Source Code Form is subject to the terms of the Mozilla Public 3 # License, v. 2.0. If a copy of the MPL was not distributed with this 4 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 6 # originally from https://hg.mozilla.org/build/tools/file/4ab9c1a4e05b/scripts/release/compare-mozconfigs.py # NOQA: E501 7 8 import difflib 9 import logging 10 import os 11 import unittest 12 13 import mozunit 14 15 here = os.path.abspath(os.path.dirname(__file__)) 16 17 FAILURE_CODE = 1 18 SUCCESS_CODE = 0 19 20 PLATFORMS = ( 21 "linux64", 22 "macosx64", 23 "win32", 24 "win64", 25 "win64-aarch64", 26 ) 27 28 log = logging.getLogger(__name__) 29 30 31 class ConfigError(Exception): 32 pass 33 34 35 def readConfig(configfile): 36 c = {} 37 with open(configfile) as config: 38 exec(config.read(), c) 39 return c["allowlist"] 40 41 42 def verify_mozconfigs( 43 mozconfig_pair, nightly_mozconfig_pair, platform, mozconfigAllowlist 44 ): 45 """Compares mozconfig to nightly_mozconfig and compare to an optional 46 allowlist of known differences. mozconfig_pair and nightly_mozconfig_pair 47 are pairs containing the mozconfig's identifier and the list of lines in 48 the mozconfig.""" 49 50 # unpack the pairs to get the names, the names are just for 51 # identifying the mozconfigs when logging the error messages 52 mozconfig_name, mozconfig_lines = mozconfig_pair 53 nightly_mozconfig_name, nightly_mozconfig_lines = nightly_mozconfig_pair 54 55 if not mozconfig_lines or not nightly_mozconfig_lines: 56 log.info("Missing mozconfigs to compare for %s" % platform) 57 return False 58 59 success = True 60 61 diff_instance = difflib.Differ() 62 diff_result = diff_instance.compare(mozconfig_lines, nightly_mozconfig_lines) 63 diff_list = list(diff_result) 64 65 for line in diff_list: 66 clean_line = line[1:].strip() 67 if (line[0] == "-" or line[0] == "+") and len(clean_line) > 1: 68 # skip comment lines 69 if clean_line.startswith("#"): 70 continue 71 # compare to allowlist 72 message = "" 73 if line[0] == "-": 74 # handle lines that move around in diff 75 if "+" + line[1:] in diff_list: 76 continue 77 if platform in mozconfigAllowlist.get("release", {}): 78 if clean_line in mozconfigAllowlist["release"][platform]: 79 continue 80 elif line[0] == "+": 81 if "-" + line[1:] in diff_list: 82 continue 83 if platform in mozconfigAllowlist.get("nightly", {}): 84 if clean_line in mozconfigAllowlist["nightly"][platform]: 85 continue 86 else: 87 log.warning( 88 "%s not in %s %s!" 89 % ( 90 clean_line, 91 platform, 92 mozconfigAllowlist["nightly"][platform], 93 ) 94 ) 95 else: 96 log.error("Skipping line %s!" % line) 97 continue 98 message = "found in %s but not in %s: %s" 99 if line[0] == "-": 100 log.error( 101 message % (mozconfig_name, nightly_mozconfig_name, clean_line) 102 ) 103 else: 104 log.error( 105 message % (nightly_mozconfig_name, mozconfig_name, clean_line) 106 ) 107 success = False 108 return success 109 110 111 def get_mozconfig(path): 112 """Consumes a path and returns a list of lines from the mozconfig file.""" 113 with open(path) as fh: 114 return fh.readlines() 115 116 117 def compare(topsrcdir): 118 app = os.path.join(topsrcdir, "browser") 119 allowlist = readConfig(os.path.join(app, "config", "mozconfigs", "allowlist")) 120 121 success = True 122 123 def normalize_lines(lines): 124 return {l.strip() for l in lines} 125 126 for platform in PLATFORMS: 127 log.info("Comparing platform %s" % platform) 128 129 mozconfigs_path = os.path.join(app, "config", "mozconfigs", platform) 130 131 nightly_path = os.path.join(mozconfigs_path, "nightly") 132 beta_path = os.path.join(mozconfigs_path, "beta") 133 release_path = os.path.join(mozconfigs_path, "release") 134 135 nightly_lines = get_mozconfig(nightly_path) 136 beta_lines = get_mozconfig(beta_path) 137 release_lines = get_mozconfig(release_path) 138 139 # Validate that entries in allowlist['nightly'][platform] are actually 140 # present. 141 allowlist_normalized = normalize_lines(allowlist["nightly"].get(platform, [])) 142 nightly_normalized = normalize_lines(nightly_lines) 143 144 for line in sorted(allowlist_normalized - nightly_normalized): 145 log.error("extra line in nightly allowlist: %s" % line) 146 success = False 147 148 log.info("Comparing beta and nightly mozconfigs") 149 passed = verify_mozconfigs( 150 (beta_path, beta_lines), (nightly_path, nightly_lines), platform, allowlist 151 ) 152 153 if not passed: 154 success = False 155 156 log.info("Comparing release and nightly mozconfigs") 157 passed = verify_mozconfigs( 158 (release_path, release_lines), 159 (nightly_path, nightly_lines), 160 platform, 161 allowlist, 162 ) 163 if not passed: 164 success = False 165 166 return success 167 168 169 class TestCompareMozconfigs(unittest.TestCase): 170 def test_compare_mozconfigs(self): 171 topsrcdir = os.path.abspath(os.path.join(here, "..", "..")) 172 self.assertTrue(compare(topsrcdir)) 173 174 175 if __name__ == "__main__": 176 logging.basicConfig(level=logging.INFO) 177 mozunit.main()