1#!/usr/bin/env python3 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 file, 4# You can obtain one at http://mozilla.org/MPL/2.0/. 5 6import pathlib 7import json 8import urllib.request 9import re 10import subprocess 11import sys 12 13 14class Logger: 15 @classmethod 16 def info(cls, s): 17 print('[INFO]', s) 18 19 # Flush to make it apeear immediately in automation log. 20 sys.stdout.flush() 21 22 @classmethod 23 def fetch(cls, url): 24 cls.info(f'Fetching {url}') 25 26 @classmethod 27 def cmd(cls, cmd): 28 def format_cmd(s): 29 if ' ' in s: 30 escaped = s.replace('"', '\"') 31 return f'"{escaped}"' 32 return s 33 34 formatted_command = ' '.join(list(map(format_cmd, cmd))) 35 cls.info(f'$ {formatted_command}') 36 37 38class GitRepository: 39 def __init__(self, path): 40 self.path = path 41 42 self.git_dir = self.path / '.git' 43 if not self.git_dir.exists(): 44 print(f'{self.path} is not a Git repository.', file=sys.stderr) 45 sys.exit(1) 46 47 def get_output(self, *args): 48 cmd = ['git'] + list(args) 49 Logger.cmd(cmd) 50 output = subprocess.run(cmd, 51 capture_output=True, 52 cwd=self.path) 53 54 return output.stdout.decode() 55 56 def run(self, *args): 57 cmd = ['git'] + list(args) 58 Logger.cmd(cmd) 59 subprocess.run(cmd, 60 check=True, 61 cwd=self.path) 62 63 def commit_message(self, rev): 64 return self.get_output('log', '-1', '--pretty=format:%s%n', rev) 65 66 67class MCRemoteRepository: 68 HG_API_URL = 'https://hg.mozilla.org/mozilla-central/' 69 70 @classmethod 71 def call(cls, name, path): 72 url = f'{cls.HG_API_URL}{name}{path}' 73 Logger.fetch(url) 74 req = urllib.request.Request(url, None, {}) 75 response = urllib.request.urlopen(req) 76 return response.read() 77 78 @classmethod 79 def call_json(cls, name, path): 80 return json.loads(cls.call(name, path)) 81 82 @classmethod 83 def file(cls, rev, path): 84 return cls.call('raw-file', f'/{rev}{path}') 85 86 87class TreeHerder: 88 API_URL = 'https://treeherder.mozilla.org/api/' 89 90 @classmethod 91 def call(cls, name): 92 url = f'{cls.API_URL}{name}' 93 Logger.fetch(url) 94 req = urllib.request.Request(url, None, { 95 'User-Agent': 'smoosh-tools', 96 }) 97 response = urllib.request.urlopen(req) 98 return response.read() 99 100 @classmethod 101 def call_json(cls, name): 102 return json.loads(cls.call(name)) 103 104 @classmethod 105 def push_id(cls, rev): 106 push = cls.call_json(f'project/mozilla-central/push/?full=true&format=json&count=1&revision={rev}') 107 return push['results'][0]['id'] 108 109 @classmethod 110 def jobs(cls, push_id): 111 push = cls.call_json(f'jobs/?push_id={push_id}&format=json') 112 count = push['count'] 113 results = [] 114 results += push['results'] 115 116 page = 2 117 while len(results) < count: 118 push = cls.call_json(f'jobs/?push_id={push_id}&format=json&page={page}') 119 results += push['results'] 120 page += 1 121 122 return results 123 124 125class Status: 126 def run(is_ci): 127 Logger.info('Fetching ci_generated branch') 128 129 jsparagus = GitRepository(pathlib.Path('./')) 130 jsparagus.run('fetch', 'origin', 'ci_generated') 131 132 Logger.info('Checking mozilla-central tip revision') 133 134 m_c_rev = MCRemoteRepository.call_json('json-log', '/tip/')['node'] 135 cargo_file = MCRemoteRepository.file( 136 m_c_rev, 137 '/js/src/frontend/smoosh/Cargo.toml' 138 ).decode() 139 m = re.search('rev = "(.+)"', cargo_file) 140 ci_generated_rev = m.group(1) 141 142 Logger.info('Checking jsparagus referred by mozilla-central') 143 144 message = jsparagus.commit_message(ci_generated_rev) 145 m = re.search('for ([A-Fa-f0-9]+)', message) 146 master_rev = m.group(1) 147 148 Logger.info('Checking build status') 149 150 push_id = TreeHerder.push_id(m_c_rev) 151 jobs = TreeHerder.jobs(push_id) 152 nonunified_job = None 153 smoosh_job = None 154 for job in jobs: 155 if 'spidermonkey-sm-nonunified-linux64/debug' in job: 156 nonunified_job = job 157 if 'spidermonkey-sm-smoosh-linux64/debug' in job: 158 smoosh_job = job 159 160 def get_result(job): 161 if job: 162 if 'completed' in job: 163 if 'success' in job: 164 return 'OK' 165 else: 166 return 'NG' 167 else: 168 return 'not yet finished' 169 else: 170 return 'unknown' 171 172 nonunified_result = get_result(nonunified_job) 173 smoosh_result = get_result(smoosh_job) 174 175 if is_ci: 176 print(f'##[set-output name=mc;]{m_c_rev}') 177 print(f'##[set-output name=jsparagus;]{master_rev}') 178 print(f'##[set-output name=build;]{nonunified_result}') 179 print(f'##[set-output name=test;]{smoosh_result}') 180 else: 181 print(f'mozilla-central tip: {m_c_rev}') 182 print(f'referred jsparagus revision: {master_rev}') 183 print(f'Build status:') 184 print(f' Build with --enable-smoosh: {nonunified_result}') 185 print(f' Test with --smoosh: {smoosh_result}') 186 187 188is_ci = False 189if len(sys.argv) > 1: 190 if sys.argv[1] == 'ci': 191 is_ci = True 192 193Status.run(is_ci) 194