app/models/beta.php (118 lines of code) (raw):

<?php declare(strict_types=1); use ReleaseInsights\{Beta, Bugzilla, Json, Request, Utils, URL}; $waiting_page = false; $lock_file = CACHE_PATH . 'beta_lock.cache'; if (! file_exists($lock_file) OR time()-filemtime($lock_file) > 900) { $waiting_page = true; Request::waitingPage('load'); } $beta = new Beta(); $uplift_counter = 0; $bug_list_details = []; foreach ($beta->uplifts() as $version => $details) { // We count all uplifts, including backouts $uplifts = count($details['total']); $uplift_counter += $uplifts; // We Query Bugzilla for bug details $bz_fields = ['id', 'summary', 'priority', 'severity', 'product', 'component', 'type']; $bug_list_details[$version] = []; if ($uplifts > 0) { $bug_list_details[$version] = Json::load( URL::Bugzilla->value . 'rest/bug?include_fields=' . implode(',', $bz_fields) . '&bug_id=' . implode('%2C', $details['total']), 3600*24 )['bugs'] ?? []; } /* The Bugzilla API does not send all bug results without auth and we don't want auth on this app. That's why we make a diff between the bugs mentionned in the logs and the ones that the Bugzilla API answers to. */ $hidden_bugs = array_values( array_diff( $details['total'], array_column($bug_list_details[$version], 'id') ) ); // Create a blank template for bugs not populated by Bugzilla $bug_template = function () use ($bz_fields) { $bug = array_flip($bz_fields); foreach ($bug as $key => $value) { $bug[$key] = 'N/A'; } return $bug; }; foreach ($hidden_bugs as $key => $value) { $hidden_bugs[$key] = $bug_template(); $hidden_bugs[$key]['id'] = $value; } $bug_list_details[$version] = [...$bug_list_details[$version], ...$hidden_bugs]; } $stats = []; foreach ($bug_list_details as $bugs) { foreach($bugs as $fields) { if (! isset($stats[$fields['product']])) { $stats[$fields['product']] = []; } $stats[$fields['product']]['bugs'][] = $fields['id']; } } arsort($stats); foreach ($stats as $product => $bugs) { $stats[$product]['bugzilla'] = Bugzilla::getBugListLink($stats[$product]['bugs']); } $known_top_crashes = [ 'IPCError-browser | ShutDownKill | mozilla::ipc::MessagePump::Run', 'IPCError-browser | ShutDownKill | NtYieldExecution', 'IPCError-browser | ShutDownKill | EMPTY: no crashing thread identified; ERROR_NO_MINIDUMP_HEADER', 'IPCError-browser | ShutDownKill', 'OOM | small', ]; $top_sigs_worth_a_bug = []; foreach ($beta->crashes() as $k => $values) { if ($k == 'summary') { continue; } foreach ($values['signatures'] as $target) { if (in_array($target['term'], $known_top_crashes)) { continue; } if (isset($top_sigs_worth_a_bug[$target['term']])){ $top_sigs_worth_a_bug[$target['term']] += $target['count']; } else { $top_sigs_worth_a_bug[$target['term']] = $target['count']; } } } // We take 10 crashes for a day as a treshold $top_sigs_worth_a_bug = array_filter($top_sigs_worth_a_bug, fn($n) => $n > 10); // We escape weird crash signature characters for url use $top_sigs_worth_a_bug = array_keys($top_sigs_worth_a_bug); $top_sigs_worth_a_bug = array_map('urlencode', $top_sigs_worth_a_bug); // Query bugs for signatures $crash_bugs = []; if (! empty($top_sigs_worth_a_bug)) { foreach ($top_sigs_worth_a_bug as $sig) { $bugs_for_top_sigs = Utils::getBugsforCrashSignature($sig)['hits'] ?? []; $tmp = array_column($bugs_for_top_sigs, 'id'); if (!empty($tmp)) { $crash_bugs[urldecode($sig)] = max( array_unique( array_column($bugs_for_top_sigs, 'id') ) ); } } } // Write a lock file for the page to keep track of its age file_put_contents($lock_file, ''); // Generate Bugzilla links per beta of all the bugs fixed/backedout $bugzilla_links = []; foreach ($bug_list_details as $version => $details) { $bugzilla_links[$version] = Bugzilla::getBugListLink(array_column($details, 'id')) . '&title=' . $version . '%20:%20Uplifts%20and%20backouts'; } if ($waiting_page) { Request::waitingPage('leave'); } return [ $beta, // this is the whole Beta object $bug_list_details, $uplift_counter, $stats, $known_top_crashes, $crash_bugs, $bugzilla_links, ];