From 4a65a014bea81384bf266330fd1e276db83dfa3f Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Mon, 16 Feb 2026 14:03:30 -0700 Subject: [PATCH 1/3] Forgot to add this to #9116 Signed-off-by: Jon Stovell --- other/prepare_release.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/other/prepare_release.php b/other/prepare_release.php index 58b04b20dc..acc7a6752c 100644 --- a/other/prepare_release.php +++ b/other/prepare_release.php @@ -55,4 +55,8 @@ } else { $updater->execute(); } + + if (shell_exec('git status --porcelain') !== null) { + die('Commit current changes, then run this script again to continue.'); + } } From 199b2763f32fdea2e3960a6325f9dad9cdab222e Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Mon, 16 Feb 2026 14:20:05 -0700 Subject: [PATCH 2/3] Improves handling of already existing "new" branch in UpdaterBase Signed-off-by: Jon Stovell --- .../AsciiTransliteratorDataUpdater.php | 2 +- other/Updaters/TimezoneDataUpdater.php | 2 +- other/Updaters/UnicodeDataUpdater.php | 2 +- other/Updaters/UpdaterBase.php | 145 +++++++++++------- other/Updaters/VersionNumberUpdater.php | 2 +- 5 files changed, 95 insertions(+), 58 deletions(-) diff --git a/other/Updaters/AsciiTransliteratorDataUpdater.php b/other/Updaters/AsciiTransliteratorDataUpdater.php index 48ec1aa7cf..6006ac62d7 100644 --- a/other/Updaters/AsciiTransliteratorDataUpdater.php +++ b/other/Updaters/AsciiTransliteratorDataUpdater.php @@ -65,7 +65,7 @@ public function execute(): void echo 'Updating AsciiTransliterator data...', PHP_EOL; } - $this->createBranch(); + $this->checkoutNewBranch(); // Do the job. $output_dir = Config::$sourcedir . '/Localization/data'; diff --git a/other/Updaters/TimezoneDataUpdater.php b/other/Updaters/TimezoneDataUpdater.php index d467f77e32..a91dd0d993 100644 --- a/other/Updaters/TimezoneDataUpdater.php +++ b/other/Updaters/TimezoneDataUpdater.php @@ -200,7 +200,7 @@ public function execute() echo 'Updating time zone data...', PHP_EOL; } - $this->createBranch(); + $this->checkoutNewBranch(); $this->fetchTzdbUpdates(); $this->updateTimezoneClass(); diff --git a/other/Updaters/UnicodeDataUpdater.php b/other/Updaters/UnicodeDataUpdater.php index 9f70cfe5db..d145cb64e0 100644 --- a/other/Updaters/UnicodeDataUpdater.php +++ b/other/Updaters/UnicodeDataUpdater.php @@ -53,7 +53,7 @@ public function execute(): void echo 'Updating Unicode data...', PHP_EOL; } - $this->createBranch(); + $this->checkoutNewBranch(); $updater = new UpdateUnicode(['files_only' => true]); $updater->execute(); diff --git a/other/Updaters/UpdaterBase.php b/other/Updaters/UpdaterBase.php index ba32ff00c4..bd69408a8d 100644 --- a/other/Updaters/UpdaterBase.php +++ b/other/Updaters/UpdaterBase.php @@ -38,6 +38,10 @@ abstract class UpdaterBase */ public const MAIN_BRANCH = 'release-3.0'; + /******************* + * Public properties + *******************/ + /** * @var string * @@ -45,6 +49,17 @@ abstract class UpdaterBase */ public string $new_branch; + /************************** + * Public static properties + **************************/ + + /** + * @var object + * + * Autoloader instance. + */ + public static object $loader; + /**************** * Public methods ****************/ @@ -79,77 +94,103 @@ public function __construct(string $new_branch) throw new \Exception('Could not continue. Dirty working tree.'); } - // Set a few variables that we'll need. - $boarddir = trim((string) shell_exec('git rev-parse --show-toplevel')); - $sourcedir = $boarddir . '/Sources'; - $vendordir = $boarddir . '/vendor'; + // Impersonate cron.php + if (!\defined('SMF')) { + \define('SMF', 'BACKGROUND'); + } + + if (!\defined('SMF_USER_AGENT')) { + \define('SMF_USER_AGENT', 'SMF'); + } - // Make sure we are working in the right directory. - chdir($boarddir); + if (!\defined('TIME_START')) { + \define('TIME_START', microtime(true)); + } - // Impersonate cron.php - \define('SMF', 'BACKGROUND'); - \define('SMF_USER_AGENT', 'SMF'); - \define('TIME_START', microtime(true)); + // Set variables, load classes, etc. + if (!isset(self::$loader)) { + // Set a few variables that we'll need. + $boarddir = trim((string) shell_exec('git rev-parse --show-toplevel')); + $sourcedir = $boarddir . '/Sources'; + $vendordir = $boarddir . '/vendor'; + + // Borrow a bit of stuff from index.php. + $index_php_start = file_get_contents($boarddir . '/index.php', false, null, 0, 4096); - // Borrow a bit of stuff from index.php. - $index_php_start = file_get_contents($boarddir . '/index.php', false, null, 0, 4096); + foreach (['SMF_VERSION', 'SMF_SOFTWARE_YEAR'] as $const) { + if (\defined($const)) { + continue; + } - foreach (['SMF_VERSION', 'SMF_SOFTWARE_YEAR'] as $const) { - preg_match("/define\('{$const}', '([^)]+)'\);/", $index_php_start, $matches); + preg_match("/define\('{$const}', '([^)]+)'\);/", $index_php_start, $matches); - if (empty($matches[1])) { - throw new \Exception("Could not find value for {$const} in index.php"); + if (empty($matches[1])) { + throw new \Exception("Could not find value for {$const} in index.php"); + } + + \define($const, $matches[1]); } - \define($const, $matches[1]); - } - - // Fire up the autoloader. - $loader = require_once $vendordir . '/autoload.php'; - $loader->setPsr4('SMF\\', $sourcedir); - $loader->setPsr4('SMF\\other\\', $boarddir . '/other'); - - // Set some more stuff we need. - Config::$boarddir = $boarddir; - Config::$sourcedir = $sourcedir; - Config::$vendordir = $vendordir; - Config::$languagesdir = Config::$boarddir . '/Languages'; - Config::$language = 'en_US'; - Config::$backward_compatibility = 0; - Config::$scripturl = 'file:///foo/bar/index.php'; - Config::$modSettings = [ - 'default_timezone' => 'UTC', - 'forum_uuid' => (string) Uuid::getNamespace(), - ]; - - Lang::$default = Config::$language; - Lang::addDirs(Config::$languagesdir); + // Fire up the autoloader. + self::$loader = require_once $vendordir . '/autoload.php'; + self::$loader->setPsr4('SMF\\', $sourcedir); + self::$loader->setPsr4('SMF\\other\\', $boarddir . '/other'); + + // Set some more stuff we need. + Config::$boarddir = $boarddir; + Config::$sourcedir = $sourcedir; + Config::$vendordir = $vendordir; + Config::$languagesdir = Config::$boarddir . '/Languages'; + Config::$language = 'en_US'; + Config::$backward_compatibility = 0; + Config::$scripturl = 'file:///foo/bar/index.php'; + Config::$modSettings = [ + 'default_timezone' => 'UTC', + 'forum_uuid' => (string) Uuid::getNamespace(), + ]; + + Lang::$default = Config::$language; + Lang::addDirs(Config::$languagesdir); + } + + // Make sure we are working in the right directory. + chdir(Config::$boarddir); } /** - * Creates a new Git branch to hold our changes. + * Checks out a new Git branch to hold our changes. * * This should always be called from within a concrete class's execute * method. */ - public function createBranch(): void + public function checkoutNewBranch(): void { - $current_branch = trim(shell_exec('git rev-parse --abbrev-ref HEAD')); - - if ($current_branch === $this->new_branch) { + // Are we already on the new branch? + if (trim(shell_exec('git rev-parse --abbrev-ref HEAD')) === $this->new_branch) { return; } - if ($current_branch !== self::MAIN_BRANCH) { - @shell_exec('git checkout "' . self::MAIN_BRANCH . '"'); - $current_branch = trim(shell_exec('git rev-parse --abbrev-ref HEAD')); + // Does the new branch already exist? + exec('git for-each-ref --format "%(refname:short)" refs/heads/', $branches); + + if (\in_array($this->new_branch, $branches)) { + @shell_exec('git checkout "' . $this->new_branch . '"'); + + if (trim(shell_exec('git rev-parse --abbrev-ref HEAD')) === $this->new_branch) { + return; + } } - if ($current_branch !== self::MAIN_BRANCH) { - throw new \Exception('Could not continue. Wrong branch is checked out.'); + // Need to create the new branch at this point, so first switch to the main branch. + if (trim(shell_exec('git rev-parse --abbrev-ref HEAD')) !== self::MAIN_BRANCH) { + @shell_exec('git checkout "' . self::MAIN_BRANCH . '"'); + + if (trim(shell_exec('git rev-parse --abbrev-ref HEAD')) !== self::MAIN_BRANCH) { + throw new \Exception('Could not continue. Wrong branch is checked out.'); + } } + // Now create the new branch. shell_exec('git checkout -b "' . $this->new_branch . '"'); if (trim(shell_exec('git rev-parse --abbrev-ref HEAD')) !== $this->new_branch) { @@ -178,11 +219,7 @@ public function hasChanged(): bool } // Are there any uncommitted changes? - if (shell_exec('git status --porcelain') !== null) { - return true; - } - - return false; + return (bool) (shell_exec('git status --porcelain') !== null); } /** diff --git a/other/Updaters/VersionNumberUpdater.php b/other/Updaters/VersionNumberUpdater.php index 49d97a6193..280f5b3171 100644 --- a/other/Updaters/VersionNumberUpdater.php +++ b/other/Updaters/VersionNumberUpdater.php @@ -136,7 +136,7 @@ public function execute(?string $new_version = null): void echo 'Updating version numbers...', PHP_EOL; } - $this->createBranch(); + $this->checkoutNewBranch(); $this->updateVersionAndYear(); $this->updateLicenseBlocks(); From ad83d8d095ccd064b8cea864e40887a7f61d0e42 Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Mon, 16 Feb 2026 14:51:00 -0700 Subject: [PATCH 3/3] Allows all updaters to run in sequence without restarting Signed-off-by: Jon Stovell --- other/Updaters/UpdaterBase.php | 15 ++++++++++++++- other/prepare_release.php | 4 ---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/other/Updaters/UpdaterBase.php b/other/Updaters/UpdaterBase.php index bd69408a8d..e760ecf93d 100644 --- a/other/Updaters/UpdaterBase.php +++ b/other/Updaters/UpdaterBase.php @@ -60,6 +60,17 @@ abstract class UpdaterBase */ public static object $loader; + /**************************** + * Internal static properties + ****************************/ + + /** + * @var bool + * + * Whether we've already checked for a dirty working tree. + */ + private static bool $checked_working_tree = false; + /**************** * Public methods ****************/ @@ -90,10 +101,12 @@ public function __construct(string $new_branch) } // Do nothing if working tree is dirty. - if (shell_exec('git status --porcelain') !== null) { + if (!self::$checked_working_tree && shell_exec('git status --porcelain') !== null) { throw new \Exception('Could not continue. Dirty working tree.'); } + self::$checked_working_tree = true; + // Impersonate cron.php if (!\defined('SMF')) { \define('SMF', 'BACKGROUND'); diff --git a/other/prepare_release.php b/other/prepare_release.php index acc7a6752c..58b04b20dc 100644 --- a/other/prepare_release.php +++ b/other/prepare_release.php @@ -55,8 +55,4 @@ } else { $updater->execute(); } - - if (shell_exec('git status --porcelain') !== null) { - die('Commit current changes, then run this script again to continue.'); - } }