app/classes/ReleaseInsights/Release.php (163 lines of code) (raw):

<?php declare(strict_types=1); namespace ReleaseInsights; use DateTime; class Release { /** @var array<int> $no_planned_dot_releases */ public array $no_planned_dot_releases = [108, 111, 115, 120]; private readonly Version $version; public function __construct( string $version, public readonly string $product_details = URL::ProductDetails->value, ) { $this->version = new Version($version); } /** * Get The schedule for a future Release * * @return array<string, string> */ public function getSchedule(): array { if ($this->version->int < 1) { return ['error' => 'Invalid version number.']; } if ($this->version->int < BETA) { return $this->getPastSchedule(); } return $this->getFutureSchedule(); } /** * Get The schedule for a future Release * * @return array<string, string> */ public function getFutureSchedule(): array { $all_releases = new Data($this->product_details)->getMajorReleases(); if (! array_key_exists($this->version->normalized, $all_releases)) { return ['error' => 'Not enough data for this version number.']; } // Future release date object $release = new DateTime($all_releases[$this->version->normalized] . ' 06:00 PST'); $nightly_target = Version::decrement($this->version->normalized, 2); // Calculate 1st day of the nightly cycle $nightly = new DateTime($all_releases[$nightly_target]); $nightly->modify('-1 day'); // Transform all the DateTime objects in the $schedule array into formated date strings $date = fn(string|object $day): string => is_object($day) ? $day->format('Y-m-d H:i:sP') : $nightly->modify($day)->format('Y-m-d H:i:sP'); $schedule = [ #️⃣ Starting with Firefox 136, QA request deadline is before the start of the nightly cycle 'qa_request_deadline' => $date($nightly->modify('-3 days')), 'nightly_start' => $date($nightly->modify('+3 days')), 'a11y_request_deadline' => $date('Friday'), 'qa_feature_done' => $date('Friday +1 week 21:00'), 'qa_test_plan_due' => $date('Friday 21:00'), 'soft_code_freeze' => $date($nightly->modify('+1 week')->modify('Thursday 08:00')), 'qa_pre_merge_done' => $date('Friday 14:00'), 'string_freeze' => $date('Friday'), 'merge_day' => $date('Monday'), 'beta_1' => $date('Monday'), 'beta_2' => $date('Wednesday 13:00'), 'beta_3' => $date('Friday 13:00'), 'sumo_1' => $date('Friday 21:00'), // Friday of Beta week 1 'beta_4' => $date('Monday 13:00'), 'beta_5' => $date('Wednesday 13:00'), 'beta_6' => $date('Friday 13:00'), 'beta_7' => $date('Monday 13:00'), 'sumo_2' => $date('Monday 21:00'), // Monday of Beta Week 3 'beta_8' => $date('Wednesday 13:00'), 'qa_pre_rc_signoff' => $date('Wednesday 14:00'), 'beta_9' => $date('Friday 13:00'), 'rc_gtb' => match ($this->version->normalized) { '147.0' => $date($nightly->modify('+1 week')->modify('Monday 21:00')), default => $date('Monday 21:00'), }, 'rc' => $date('Tuesday'), 'release' => $date($release->setTimezone(new \DateTimeZone('UTC'))), ]; // Add the Android weekly release before the planned dot release mid-cycle if (! in_array($this->version->int, $this->no_planned_dot_releases)) { $schedule += ['mobile_dot_release' => $date($release->modify('+1 week 00:00'))]; } // Add the planned dot release mid-cycle if (! in_array($this->version->int, $this->no_planned_dot_releases)) { if ($this->version->normalized === '146.0') { $schedule += ['planned_dot_release' => $date($release->modify('December 18 00:00'))]; } else { $schedule += ['planned_dot_release' => $date($release->modify('+1 week 00:00'))]; } } // Sort the schedule by date, needed for schedules with a fixup asort($schedule); // Dev mode: assert that qa_pre_rc_signoff happens before the last beta // If not, this causes a 1 week shift in rc_gtb calculation $post_qa_step = array_search('qa_pre_rc_signoff', array_keys($schedule)) + 1; assert( str_starts_with( array_keys($schedule)[$post_qa_step], 'beta' ) === true ); // The schedule starts with the release version number return ['version' => $this->version->normalized] + $schedule; } /** * Get The schedule for a past Release * * @return array<string, string> */ public function getPastSchedule() : array { $data = new Data($this->product_details); $releases = $data->getMajorReleases(); $release = new DateTime($releases[$this->version->normalized] . ' 06:00 PST'); $betas = $data->getPastBetas(); $betas = array_filter( $betas, fn($k) => str_starts_with($k, $this->version->normalized), ARRAY_FILTER_USE_KEY ); $dot_releases = $data->getDesktopPastReleases(); $dot_releases = array_filter( $dot_releases, fn($k) => $k != $this->version->normalized && str_starts_with($k, $this->version->normalized), ARRAY_FILTER_USE_KEY ); // Transform all the DateTime objects in the $schedule array into formated date strings $format_date = fn(string $day): string => new DateTime($day)->format('Y-m-d H:i:sP'); $schedule = [ 'nightly_start' => $format_date(Nightly::cycleStart($this->version->int)), ]; $count = 0; foreach ($betas as $k => $date) { $count++; $schedule['beta_' . (string) $count] = $format_date($date); } $schedule += [ 'release' => $release->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:sP'), ]; $count = 0; foreach ($dot_releases as $date) { $count++; $schedule['dot_release_' . (string) $count] = $format_date($date); } // The schedule starts with the release version number return ['version' => $this->version->normalized] + $schedule; } public static function getNiceLabel(string $version, string $label, bool $short=true): string { $short_version = (string) (int) $version; $labels = [ 'qa_request_deadline' => $short_version . ' QA request deadline', 'nightly_start' => 'Nightly ' . $short_version . ' starts', 'a11y_request_deadline' => $short_version . ' a11y engineering request deadline for Nightly', 'qa_feature_done' => $short_version .' build ready for QA', #️⃣ AKA Feature Complete Milestone 'qa_feature_done_1' => $short_version .' build ready for nightly QA', 'qa_feature_done_2' => $short_version .' build ready for beta QA', 'soft_code_freeze' => ($short ? '' : 'Firefox ') . $short_version . ' soft Code Freeze starts at 08:00 UTC', 'qa_pre_merge_done' => $short_version . ' regression testing completed', 'qa_test_plan_due' => 'Final deadline for QA’s Feature Test Plan approval', 'string_freeze' => 'String Freeze' . ($short ? '' : ' starts'), 'merge_day' => 'Merge day', 'beta_1' => ($short ? '' : 'Firefox ') . $short_version . ' b1 GTB', 'beta_2' => ($short ? '' : 'Firefox ') . $short_version . ' b2' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_3' => ($short ? '' : 'Firefox ') . $short_version . ' b3' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'sumo_1' => 'SUMO deadline', 'beta_4' => ($short ? '' : 'Firefox ') . $short_version . ' b4' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_5' => ($short ? '' : 'Firefox ') . $short_version . ' b5' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_6' => ($short ? '' : 'Firefox ') . $short_version . ' b6' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_7' => ($short ? '' : 'Firefox ') . $short_version . ' b7' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'sumo_2' => 'SUMO content localization starts', 'beta_8' => ($short ? '' : 'Firefox ') . $short_version . ' b8' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'qa_pre_rc_signoff' => $short_version . ' QA sign off', 'beta_9' => ($short ? '' : 'Firefox ') . $short_version . ' b9' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_10' => ($short ? '' : 'Firefox ') . $short_version . ' b10' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_11' => ($short ? '' : 'Firefox ') . $short_version . ' b11' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_12' => ($short ? '' : 'Firefox ') . $short_version . ' b12' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_13' => ($short ? '' : 'Firefox ') . $short_version . ' b13' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_14' => ($short ? '' : 'Firefox ') . $short_version . ' b14' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'beta_15' => ($short ? '' : 'Firefox ') . $short_version . ' b15' . ($short ? ' GTB' : ' (GTB: 13:00 UTC)'), 'rc_gtb' => ($short ? '' : 'Firefox ') . $short_version . ' go to Build', 'rc' => ($short ? '' : 'Firefox ') . 'RC', 'release' => ($short ? '' : 'Firefox ') . $short_version . ($short ? ' Release' : ' go-live @ 6AM PT'), 'mobile_dot_release' => ($short ? 'Potential mobile ' : 'Potential mobile ') . $version . ($short ? '.x' : ' dot release'), 'planned_dot_release' => ($short ? 'Planned ' : 'Planned Firefox ') . $version . ($short ? '.x' : ' dot release'), ]; return $labels[$label]; } }