diff --git a/admin/create-new-changelog.php b/admin/create-new-changelog.php index cc7f8359a570..eca1696676b2 100644 --- a/admin/create-new-changelog.php +++ b/admin/create-new-changelog.php @@ -9,81 +9,90 @@ 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); } + +// 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, '/\.\. 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}\""); +} 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 new file mode 100644 index 000000000000..fc348565464f --- /dev/null +++ b/tests/system/AutoReview/CreateNewChangelogTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\AutoReview; + +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; + + 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>&1', $output, $exitCode); + + if ($exitCode !== 0) { + $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, + // 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} 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'), + ); + + $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 => $this->fail('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; + } +}