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()