app/models/nightly.php (198 lines of code) (raw):

<?php declare(strict_types=1); use BzKarma\Scoring; use ReleaseInsights\{Bugzilla, Json, URL, Request, Utils}; Request::waitingPage('load'); /* We need previous and next days for navigation and changelog The requester date is already in the $date variable */ $today = date('Ymd'); $requested_date = Utils::getDate(); $previous_date = date('Ymd', strtotime($requested_date . ' -1 day')); $next_date = date('Ymd', strtotime($requested_date . ' +1 day')); // We may have to display a warning message because an external resource is down $warning = ''; // Get nightlies for the GET Request (or today's nightly) $nightlies = include MODELS . 'api/nightly.php'; // Store a value for the View title $display_date = strtotime($requested_date); $fallback_nightly = false; // This is a fallback mechanism for Buildhub which sometimes takes hours to have the latest nightly if (empty($nightlies)) { // Get the latest nightly build ID, used as a tooltip on the nightly version number $latest_nightly = Json::load( URL::Archive->value . 'pub/firefox/nightly/latest-mozilla-central/firefox-' . FIREFOX_NIGHTLY . '.en-US.win64.json', 900 ); // We want to make sure that the latest nightly from archive.mozilla.org is not yesterday's nightly if (isset($latest_nightly['buildid']) && $today === date('Ymd', strtotime((string) $latest_nightly['buildid']))) { $nightlies = [ $latest_nightly['buildid'] => [ 'revision' => $latest_nightly['moz_source_stamp'], 'version' => FIREFOX_NIGHTLY, ], ]; $fallback_nightly = true; } unset($latest_nightly); } // We now fetch the previous day nightlies because we need them for changelogs $_GET['date'] = $previous_date; $nightlies_day_before = include MODELS . 'api/nightly.php'; /* If we didn't ship any nightly the day before, check previous days. We don't go further than 7 days back. */ if (empty($nightlies_day_before)) { foreach(range(1,7) as $i) { $_GET['date'] = date('Ymd', strtotime($requested_date . " -{$i} days")); $nightlies_day_before = include MODELS . 'api/nightly.php'; if (! empty($nightlies_day_before)) { break; } } } // Associate nightly with nightly-1 $nightly_pairs = []; $i = true; $previous_changeset = null; foreach ($nightlies as $buildid => $changeset) { // The first build of the day is to associate with yesterday's last build if ($i === true) { $nightly_pairs[] = [ 'buildid' => $buildid, 'changeset' => $changeset['revision'], 'version' => $changeset['version'], 'prev_changeset' => end($nightlies_day_before)['revision'], ]; $i = false; $previous_changeset = $changeset['revision']; continue; } $nightly_pairs[] = [ 'buildid' => $buildid, 'changeset' => $changeset['revision'], 'version' => $changeset['version'], 'prev_changeset' => $previous_changeset, ]; $previous_changeset = $changeset['revision']; } $build_crashes = []; $top_sigs = []; // We fetch crashes from Socorro for the last 10 days only $days_elapsed = date_diff(date_create(date($today)), date_create($requested_date))->days; if ($days_elapsed < 10) { foreach ($nightly_pairs as $dataset) { $build_crashes[$dataset['buildid']] = Utils::getCrashesForBuildID($dataset['buildid'])['total'] ?? null; if (is_null($build_crashes[$dataset['buildid']])) { $warning = "Socorro is not providing data"; continue; } } foreach ($nightly_pairs as $dataset) { $sig = Utils::getCrashesForBuildID($dataset['buildid'])['facets']['signature'] ?? null; if (is_null($sig)) { $warning = "Socorro is not providing data"; continue; } $top_sigs[$dataset['buildid']] = array_splice($sig, 0, 20); } } $bug_list = []; $bug_list_karma = []; $bug_list_karma_details = []; foreach ($nightly_pairs as $dataset) { $bugs = Bugzilla::getBugsFromHgWeb( URL::Mercurial->value . 'mozilla-central/json-pushes?fromchange=' . $dataset['prev_changeset'] . '&tochange=' . $dataset['changeset'] . '&full&version=2' )['total']; $bug_list_karma = array_unique([...$bugs,...$bug_list_karma]); // There were no bugs in the build, it is the same as the previous one if (empty($bugs)) { $bug_list[$dataset['buildid']] = [ 'bugs' => null, 'url' => '', 'count' => 0, ]; continue; } $url = Bugzilla::getBugListLink($bugs); // Bugzilla REST API https://wiki.mozilla.org/Bugzilla:REST_API $bug_list_details = Json::load(URL::Bugzilla->value . 'rest/bug?include_fields=id,summary,priority,severity,keywords,product,component,type,duplicates,regressions,cf_webcompat_priority,cf_performance_impact,cf_tracking_firefox' . NIGHTLY . ',cf_tracking_firefox' . BETA . ',cf_tracking_firefox' . RELEASE . ',cf_status_firefox' . NIGHTLY . ',cf_status_firefox' . BETA . ',cf_status_firefox' . RELEASE . ',cc,see_also&bug_id=' . implode('%2C', $bugs)); $bug_list[$dataset['buildid']] = [ 'bugs' => $bug_list_details['bugs'], 'url' => $url, 'count' => is_countable($bugs) ? count($bugs) : 0, ]; $bug_list_karma_details = [...$bug_list_details['bugs'], ...$bug_list_karma_details]; } // Create the real bug list Karma sort($bug_list_karma); $bug_list_karma = array_map('intval', $bug_list_karma); $bug_list_karma = array_values($bug_list_karma); $bug_list_karma = array_flip($bug_list_karma); $scores = new Scoring($bug_list_karma_details, RELEASE); // The $bug_list_karma array has bug numbers as keys and score (ints) as values foreach ($bug_list_karma as $key => $value) { $bug_list_karma[$key] = [ 'score' => $scores->getBugScore($key), 'details' => $scores->getBugScoreDetails($key), ]; } $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 ($top_sigs as $values) { foreach ($values 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, 30)['hits']; // short 30s cache intended $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') ) ); } } } // In this section, we extract outstanding bugs $outstanding_bugs = []; foreach ($bug_list as $key => $values) { if (empty($values['bugs'])) { // We may have a build with no patch from Bugzilla continue; } foreach ($values['bugs'] as $bug_details) { // Old bugs fixed are often interesting if ($bug_details['id'] < 1_500_000) { $outstanding_bugs[$key]['bugs'][] = $bug_details; continue; } // Enhancements are potentiol release notes additions if ($bug_details['type'] == 'enhancement') { $outstanding_bugs[$key]['bugs'][] = $bug_details; continue; } // High karma if ($bug_list_karma[$bug_details['id']]['score'] > 15) { $outstanding_bugs[$key]['bugs'][] = $bug_details; } } } Request::waitingPage('hide'); return [ $display_date, $nightly_pairs, $build_crashes, $top_sigs, $crash_bugs, $bug_list, $bug_list_karma, $outstanding_bugs, $previous_date, $requested_date, $next_date, $today, $known_top_crashes, $fallback_nightly, $warning, ];