Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 50 additions & 41 deletions admin/create-new-changelog.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]} <current_version> <new_version>" . 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]} <current_version> <new_version> [--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}\"");
}
2 changes: 1 addition & 1 deletion system/CodeIgniter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
148 changes: 148 additions & 0 deletions tests/system/AutoReview/CreateNewChangelogTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* 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<string, array{0: string}>
*/
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;
}
}
Loading