From ab6469e981c3b37dadd043cf85891f33c58ed422 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 14 Feb 2026 23:53:53 +0800 Subject: [PATCH 1/5] Refactor for better variable names --- admin/create-new-changelog.php | 84 +++++++++++++++++----------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/admin/create-new-changelog.php b/admin/create-new-changelog.php index cc7f8359a570..f109230e307d 100644 --- a/admin/create-new-changelog.php +++ b/admin/create-new-changelog.php @@ -9,81 +9,83 @@ function replace_file_content(string $path, string $pattern, string $replace): v file_put_contents($path, $output); } -// Main. chdir(__DIR__ . '/..'); -if ($argc !== 3) { - echo "Usage: php {$argv[0]} " . PHP_EOL; - echo "E.g.,: php {$argv[0]} 4.4.3 4.4.4" . PHP_EOL; +if (! isset($argv[1]) || ! isset($argv[2])) { + echo "Usage: php {$argv[0]} [--dry-run]\n"; + echo "E.g. : php {$argv[0]} 4.4.3 4.4.4 --dry-run\n"; exit(1); } // Gets version number from argument. -$versionCurrent = $argv[1]; // e.g., '4.4.3' -$versionCurrentParts = explode('.', $versionCurrent); -$minorCurrent = $versionCurrentParts[0] . '.' . $versionCurrentParts[1]; -$version = $argv[2]; // e.g., '4.4.4' -$versionParts = explode('.', $version); -$minor = $versionParts[0] . '.' . $versionParts[1]; -$isMinorUpdate = ($minorCurrent !== $minor); - -// Creates a branch for release. -if (! $isMinorUpdate) { - system('git switch develop'); +$currentVersion = $argv[1]; // e.g., '4.4.3' +$currentVersionParts = explode('.', $currentVersion, 3); +$currentMinorVersion = $currentVersionParts[0] . '.' . $currentVersionParts[1]; +$newVersion = $argv[2]; // e.g., '4.4.4' +$newVersionParts = explode('.', $newVersion, 3); +$newMinorVersion = $newVersionParts[0] . '.' . $newVersionParts[1]; +$isMinorUpdate = $currentMinorVersion !== $newMinorVersion; + +// Creates a branch for release +if (! in_array('--dry-run', $argv, true)) { + if (! $isMinorUpdate) { + system('git switch develop'); + } + + system("git switch -c docs-changelog-{$newVersion}"); + system("git switch docs-changelog-{$newVersion}"); } -system('git switch -c docs-changelog-' . $version); -system('git switch docs-changelog-' . $version); // Copy changelog -$changelog = "./user_guide_src/source/changelogs/v{$version}.rst"; +$newChangelog = "./user_guide_src/source/changelogs/v{$newVersion}.rst"; $changelogIndex = './user_guide_src/source/changelogs/index.rst'; + if ($isMinorUpdate) { - copy('./admin/next-changelog-minor.rst', $changelog); + copy('./admin/next-changelog-minor.rst', $newChangelog); } else { - copy('./admin/next-changelog-patch.rst', $changelog); + copy('./admin/next-changelog-patch.rst', $newChangelog); } + // Add changelog to index.rst. replace_file_content( $changelogIndex, '/\.\. toctree::\n :titlesonly:\n/u', - ".. toctree::\n :titlesonly:\n\n v{$version}", + ".. toctree::\n :titlesonly:\n\n v{$newVersion}", ); + // Replace {version} -$length = mb_strlen("Version {$version}"); -$underline = str_repeat('#', $length); +$underline = str_repeat('#', mb_strlen("Version {$newVersion}")); replace_file_content( - $changelog, + $newChangelog, '/#################\nVersion {version}\n#################/u', - "{$underline}\nVersion {$version}\n{$underline}", -); -replace_file_content( - $changelog, - '/{version}/u', - "{$version}", + "{$underline}\nVersion {$newVersion}\n{$underline}", ); +replace_file_content($newChangelog, '/{version}/u', $newVersion); // Copy upgrading -$versionWithoutDots = str_replace('.', '', $version); -$upgrading = "./user_guide_src/source/installation/upgrade_{$versionWithoutDots}.rst"; +$versionWithoutDots = str_replace('.', '', $newVersion); +$newUpgrading = "./user_guide_src/source/installation/upgrade_{$versionWithoutDots}.rst"; $upgradingIndex = './user_guide_src/source/installation/upgrading.rst'; -copy('./admin/next-upgrading-guide.rst', $upgrading); +copy('./admin/next-upgrading-guide.rst', $newUpgrading); + // Add upgrading to upgrading.rst. replace_file_content( $upgradingIndex, '/ backward_compatibility_notes\n/u', " backward_compatibility_notes\n\n upgrade_{$versionWithoutDots}", ); + // Replace {version} -$length = mb_strlen("Upgrading from {$versionCurrent} to {$version}"); -$underline = str_repeat('#', $length); +$underline = str_repeat('#', mb_strlen("Upgrading from {$currentVersion} to {$newVersion}")); replace_file_content( - $upgrading, + $newUpgrading, '/##############################\nUpgrading from {version} to {version}\n##############################/u', - "{$underline}\nUpgrading from {$versionCurrent} to {$version}\n{$underline}", + "{$underline}\nUpgrading from {$currentVersion} to {$newVersion}\n{$underline}", ); -// Commits -system("git add {$changelog} {$changelogIndex}"); -system("git add {$upgrading} {$upgradingIndex}"); -system('git commit -m "docs: add changelog and upgrade for v' . $version . '"'); +if (! in_array('--dry-run', $argv, true)) { + system("git add {$newChangelog} {$changelogIndex}"); + system("git add {$newUpgrading} {$upgradingIndex}"); + system("git commit -m \"docs: add changelog and upgrade for v{$newVersion}\""); +} From 0cccba00426ef8c2ef3a7a92f182fe08d7fcc992 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 14 Feb 2026 23:54:47 +0800 Subject: [PATCH 2/5] Replace `CodeIgniter::CI_VERSION` with dev version --- admin/create-new-changelog.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/admin/create-new-changelog.php b/admin/create-new-changelog.php index f109230e307d..eca1696676b2 100644 --- a/admin/create-new-changelog.php +++ b/admin/create-new-changelog.php @@ -47,6 +47,13 @@ function replace_file_content(string $path, string $pattern, string $replace): v copy('./admin/next-changelog-patch.rst', $newChangelog); } +// Replace version in CodeIgniter.php to {version}-dev. +replace_file_content( + './system/CodeIgniter.php', + '/public const CI_VERSION = \'.*?\';/u', + "public const CI_VERSION = '{$newVersion}-dev';", +); + // Add changelog to index.rst. replace_file_content( $changelogIndex, From e8c31c98d2bb58bd0984e02b7a7a89a0e97d071f Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 15 Feb 2026 00:42:20 +0800 Subject: [PATCH 3/5] Add auto review test --- .../AutoReview/CreateNewChangelogTest.php | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 tests/system/AutoReview/CreateNewChangelogTest.php diff --git a/tests/system/AutoReview/CreateNewChangelogTest.php b/tests/system/AutoReview/CreateNewChangelogTest.php new file mode 100644 index 000000000000..094854a8569b --- /dev/null +++ b/tests/system/AutoReview/CreateNewChangelogTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\AutoReview; + +use InvalidArgumentException; +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; + +/** + * @internal + */ +#[CoversNothing] +#[Group('AutoReview')] +final class CreateNewChangelogTest extends TestCase +{ + private string $currentVersion; + + protected function setUp(): void + { + parent::setUp(); + + exec('git describe --tags --abbrev=0 2>/dev/null', $output, $exitCode); + + if ($exitCode !== 0) { + $this->markTestSkipped('Unable to determine the current version from git tags.'); + } + + // Current tag should already have the next patch docs done, so for testing purposes, + // we will treat the next patch version as the current version. + $this->currentVersion = $this->incrementVersion(trim($output[0], 'v')); + } + + #[DataProvider('provideCreateNewChangelog')] + public function testCreateNewChangelog(string $mode): void + { + $currentVersion = $this->currentVersion; + $newVersion = $this->incrementVersion($currentVersion, $mode); + + exec( + sprintf('php ./admin/create-new-changelog.php %s %s --dry-run', $currentVersion, $newVersion), + $output, + $exitCode, + ); + + $this->assertSame(0, $exitCode, "Script exited with code {$exitCode}. Output: " . implode("\n", $output)); + + $this->assertStringContainsString( + "public const CI_VERSION = '{$newVersion}-dev';", + $this->getContents('./system/CodeIgniter.php'), + ); + + $this->assertFileExists("./user_guide_src/source/changelogs/v{$newVersion}.rst"); + $this->assertStringContainsString( + "Version {$newVersion}", + $this->getContents("./user_guide_src/source/changelogs/v{$newVersion}.rst"), + ); + $this->assertStringContainsString( + $newVersion, + $this->getContents('./user_guide_src/source/changelogs/index.rst'), + ); + + $versionWithoutDots = str_replace('.', '', $newVersion); + $this->assertFileExists("./user_guide_src/source/installation/upgrade_{$versionWithoutDots}.rst"); + $this->assertStringContainsString( + "Upgrading from {$currentVersion} to {$newVersion}", + $this->getContents("./user_guide_src/source/installation/upgrade_{$versionWithoutDots}.rst"), + ); + $this->assertStringContainsString( + "upgrade_{$versionWithoutDots}", + $this->getContents('./user_guide_src/source/installation/upgrading.rst'), + ); + + // cleanup added and modified files + exec('git restore .'); + exec('git clean -fd'); + } + + /** + * @return iterable + */ + public static function provideCreateNewChangelog(): iterable + { + yield 'patch update' => ['patch']; + + yield 'minor update' => ['minor']; + + yield 'major update' => ['major']; + } + + private function incrementVersion(string $version, string $mode = 'patch'): string + { + $parts = explode('.', $version); + + return match ($mode) { + 'major' => sprintf('%d.0.0', ++$parts[0]), + 'minor' => sprintf('%d.%d.0', $parts[0], ++$parts[1]), + 'patch' => sprintf('%d.%d.%d', $parts[0], $parts[1], ++$parts[2]), + default => throw new InvalidArgumentException('Invalid version increment mode. Use "major", "minor", or "patch".'), + }; + } + + private function getContents(string $path): string + { + $contents = @file_get_contents($path); + + if ($contents === false) { + $this->fail("Failed to read file contents from {$path}."); + } + + return $contents; + } +} From d572b9600c29246d9d7e5bd84c24061be8945b1a Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 15 Feb 2026 00:43:00 +0800 Subject: [PATCH 4/5] Change CI_VERSION --- system/CodeIgniter.php | 2 +- tests/system/AutoReview/CreateNewChangelogTest.php | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index d59ebd61c5c8..b2cbbf802f29 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -55,7 +55,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.7.0'; + public const CI_VERSION = '4.7.1-dev'; /** * App startup time. diff --git a/tests/system/AutoReview/CreateNewChangelogTest.php b/tests/system/AutoReview/CreateNewChangelogTest.php index 094854a8569b..534690f0a649 100644 --- a/tests/system/AutoReview/CreateNewChangelogTest.php +++ b/tests/system/AutoReview/CreateNewChangelogTest.php @@ -1,5 +1,7 @@ getContents("./user_guide_src/source/changelogs/v{$newVersion}.rst"), ); + $this->assertStringContainsString( + "**{$newVersion} release of CodeIgniter4**", + $this->getContents("./user_guide_src/source/changelogs/v{$newVersion}.rst"), + ); $this->assertStringContainsString( $newVersion, $this->getContents('./user_guide_src/source/changelogs/index.rst'), @@ -106,7 +111,7 @@ private function incrementVersion(string $version, string $mode = 'patch'): stri 'major' => sprintf('%d.0.0', ++$parts[0]), 'minor' => sprintf('%d.%d.0', $parts[0], ++$parts[1]), 'patch' => sprintf('%d.%d.%d', $parts[0], $parts[1], ++$parts[2]), - default => throw new InvalidArgumentException('Invalid version increment mode. Use "major", "minor", or "patch".'), + default => $this->fail('Invalid version increment mode. Use "major", "minor", or "patch".'), }; } From 1b6ca6d64d8908c13223022e0ee7f0da41485a64 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 15 Feb 2026 02:02:49 +0800 Subject: [PATCH 5/5] Fetch tags manually --- .../AutoReview/CreateNewChangelogTest.php | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/system/AutoReview/CreateNewChangelogTest.php b/tests/system/AutoReview/CreateNewChangelogTest.php index 534690f0a649..fc348565464f 100644 --- a/tests/system/AutoReview/CreateNewChangelogTest.php +++ b/tests/system/AutoReview/CreateNewChangelogTest.php @@ -27,14 +27,34 @@ final class CreateNewChangelogTest extends TestCase { private string $currentVersion; + public static function setUpBeforeClass(): void + { + parent::setUpBeforeClass(); + + if (getenv('GITHUB_ACTIONS') !== false) { + exec('git fetch --unshallow 2>&1', $output, $exitCode); + exec('git fetch --tags 2>&1', $output, $exitCode); + + if ($exitCode !== 0) { + self::fail(sprintf( + "Failed to fetch git history and tags.\nOutput: %s", + implode("\n", $output), + )); + } + } + } + protected function setUp(): void { parent::setUp(); - exec('git describe --tags --abbrev=0 2>/dev/null', $output, $exitCode); + exec('git describe --tags --abbrev=0 2>&1', $output, $exitCode); if ($exitCode !== 0) { - $this->markTestSkipped('Unable to determine the current version from git tags.'); + $this->markTestSkipped(sprintf( + "Unable to get the latest git tag.\nOutput: %s", + implode("\n", $output), + )); } // Current tag should already have the next patch docs done, so for testing purposes,