github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/util/pull_run_into_static.py (about) 1 #!/usr/bin/env python 2 3 # Copyright 2017 The WPT Dashboard Project. All rights reserved. 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 7 """ 8 Tool for pulling a local copy of the JSON for a given run ,and saving the files 9 in the 'static' dir. This tool is intended for updating the pre-population 10 output of /util/populate_dev_data.py, and a change including the content 11 generated by this tool should also update the content in that script. 12 13 Example usage: 14 ./pull_run_into_static.py \ 15 --reset \ 16 --sha=24278ab617 \ 17 chrome-63.0-linux \ 18 edge-15-windows \ 19 firefox-57.0-linux \ 20 safari-10-macos 21 """ 22 23 import argparse 24 import inspect 25 import json 26 import logging 27 import os 28 import shutil 29 from urllib.parse import urlencode 30 import urllib3 31 32 here = os.path.dirname(__file__) 33 34 35 def main(): 36 args = parse_flags() # type: argparse.Namespace 37 38 loggingLevel = getattr(logging, args.log.upper(), None) 39 logging.basicConfig(level=loggingLevel) 40 41 # Counters for overview. 42 skipped = 0 43 failed = 0 44 processed = 0 45 46 sha = args.sha # type: str 47 48 staticFilePath = os.path.abspath(os.path.join(here, '..', 'static', sha)) 49 logging.info('Operating in directory %s' % (staticFilePath)) 50 51 if os.path.exists(staticFilePath) and args.reset: 52 logging.warning('Removing directory and contents') 53 if not args.dry: 54 shutil.rmtree(staticFilePath) 55 56 pool = urllib3.PoolManager() # type: urllib3.PoolManager 57 for platform in args.platforms: # type: str 58 encodedArgs = urlencode({'sha': sha, 'platform': platform}) 59 url = 'https://wpt.fyi/results?' + encodedArgs 60 61 # type: urllib3.response.HTTPResponse 62 response = pool.request('GET', url, 63 redirect=False) 64 65 if response.status // 100 != 3: 66 logging.warning( 67 'Got unexpected non-redirect result %d' % (response.status)) 68 continue 69 70 loadedUrl = response.headers['location'] 71 response = pool.request('GET', loadedUrl) 72 73 if response.status != 200: 74 logging.warning('Failed to fetch %s' % (url)) 75 continue 76 logging.debug('Processing JSON from %s' % (url)) 77 tests = json.loads(response.data.decode('utf-8')) 78 79 gzipFilenamePart = loadedUrl.split('/')[-1] 80 filename = os.path.join(staticFilePath, gzipFilenamePart) 81 82 if not os.path.exists(filename): 83 write_file(response.data, filename, args.dry) 84 85 # Let the requests rain down. 86 for key in tests.keys(): 87 encodedArgs = urlencode( 88 {'sha': sha, 'platform': platform, 'test': key[1:]}) 89 testUrl = 'https://wpt.fyi/results?' + encodedArgs 90 try: 91 filename = os.path.join(staticFilePath, platform, key[1:]) 92 if os.path.exists(filename): 93 skipped += 1 94 logging.info( 95 'Skipping file %s which already exists.' % (filename)) 96 continue 97 98 logging.info('Fetching %s...' % (testUrl)) 99 100 # type: urllib3.response.HTTPResponse 101 response = pool.request('GET', testUrl) 102 if response.status != 200: 103 failed += 1 104 logging.warning('Failed to fetch %s' % (testUrl)) 105 continue 106 107 write_file(response.data, filename, args.dry) 108 processed += 1 109 110 except IOError as e: 111 logging.warning('IOError processing %s: %s' % (testUrl, e)) 112 failed += 1 113 114 msg = 'Completed pull with %d files processed, %d skipped and %d failures.' 115 logging.info(msg % (processed, skipped, failed)) 116 117 118 # Create an ArgumentParser for the flags we'll expect. 119 def parse_flags(): # type: () -> argparse.Namespace 120 # Re-use the docs above as the --help output. 121 parser = argparse.ArgumentParser(description=inspect.cleandoc(__doc__)) 122 parser.add_argument( 123 '--sha', 124 default='latest', 125 help='SHA[0:10] of the run to fetch') 126 parser.add_argument( 127 '--log', 128 type=str, 129 default='INFO', 130 help='Log level to output') 131 parser.add_argument( 132 '--reset', 133 dest='reset', 134 action='store_true', 135 help='Clears any existing /static/{SHA} directory') 136 parser.add_argument( 137 '--dry', 138 dest='dry', 139 action='store_true', 140 help='Do a dry run (don\'t actually write any files)') 141 parser.add_argument( 142 'platforms', 143 type=str, 144 nargs='+', 145 metavar='platform', 146 help='Platforms to fetch the runs for, e.g. "safari-10.0"') 147 return parser.parse_args() 148 149 150 # Log dir creations and writes for the given file creation, and actually do it 151 # when not in a dry run. 152 def write_file(jsonData, # type: bytes 153 filename, # type: str 154 dryRun # type: bool 155 ): 156 filepath = os.path.dirname(filename) 157 if not os.path.exists(filepath): 158 logging.debug('Creating directory %s' % (filepath)) 159 if not dryRun: 160 os.makedirs(filepath) 161 162 logging.info('Writing content to %s' % (filename)) 163 if not dryRun: 164 with open(filename, 'wb') as file: 165 file.write(jsonData) 166 167 168 if __name__ == '__main__': 169 main()