From 1b28ae9203374fc1a7ecc8ee80ba8bdc07b2f771 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 3 Nov 2026 21:28:08 +0800 Subject: [PATCH] chore: port the release tooling to Markdown --- .github/scripts/validate-version | 9 +--- admin/RELEASE.md | 15 +++--- admin/create-new-changelog.php | 42 +++++++--------- admin/generate-changelog.php | 18 ++----- admin/next-changelog-minor.md | 49 +++++++++++++++++++ admin/next-changelog-patch.md | 17 +++++++ admin/next-upgrading-guide.md | 34 +++++++++++++ admin/prepare-release.php | 15 ++---- admin/update-upgrade-guide.php | 18 ++++--- .../AutoReview/CreateNewChangelogTest.php | 29 +++++++---- .../AutoReview/GenerateChangelogTest.php | 6 +-- .../AutoReview/UpdateUpgradeGuideTest.php | 4 +- 12 files changed, 173 insertions(+), 83 deletions(-) create mode 100644 admin/next-changelog-minor.md create mode 100644 admin/next-changelog-patch.md create mode 100644 admin/next-upgrading-guide.md diff --git a/.github/scripts/validate-version b/.github/scripts/validate-version index 411900dedb75..308a807a2799 100644 --- a/.github/scripts/validate-version +++ b/.github/scripts/validate-version @@ -6,14 +6,9 @@ if [[ -z $1 ]]; then exit 1 fi -FILES=("system/CodeIgniter.php" "user_guide_src/source/conf.py") -LENGTH="${#FILES[@]}" +CHANGELOG="docs/changelogs/v$1.md" -for FILE in "${FILES[@]}"; do - COUNT="$((COUNT + $(grep -c "$FILE" -e "'$1'")))" -done - -if [[ $COUNT -ne $LENGTH ]]; then +if ! grep -q -e "'$1'" system/CodeIgniter.php || [[ ! -f $CHANGELOG ]] || grep -q -e "^Release Date: Unreleased" "$CHANGELOG"; then echo "CodeIgniter version is not updated to v"$1"" exit 1 fi diff --git a/admin/RELEASE.md b/admin/RELEASE.md index 9bd85c946f7e..aba653752e43 100644 --- a/admin/RELEASE.md +++ b/admin/RELEASE.md @@ -216,7 +216,7 @@ Lines that start with `D` are removed files. Make sure none of them is needed by Skip this step if the release has no security fixes. -Merge any security advisory pull requests from the private forks. Each fix must also be documented in the `SECURITY` section of **user_guide_src/source/changelogs/v$VERSION.rst**, because step 7 copies that section into **CHANGELOG.md**. +Merge any security advisory pull requests from the private forks. Each fix must also be documented in the `SECURITY` section of **docs/changelogs/v$VERSION.md**, because step 7 copies that section into **CHANGELOG.md**. ### 7. Generate `CHANGELOG.md` @@ -244,12 +244,12 @@ Copy the result into **CHANGELOG.md** and match the format of the existing entri ### 8. Update the detailed changelog -Edit **user_guide_src/source/changelogs/v$VERSION.rst**. +Edit **docs/changelogs/v$VERSION.md**. Remove every section title that has no items under it. ### 9. Update the upgrade guide -Edit the upgrade guide page for the version in **user_guide_src/source/installation/**. The file name is `upgrade_` followed by the version without dots, so `4.7.6` is **upgrade_476.rst**. +Edit the upgrade guide page for the version in **docs/installation/**. The file name is `upgrade_` followed by the version without dots, so `4.7.6` is **upgrade_476.md**. Preview the generated content: @@ -284,8 +284,8 @@ The script does the following: - Creates the branch `release-$VERSION` from `develop`, replacing any local branch of the same name. - Sets `CI_VERSION` in **system/CodeIgniter.php** to `$VERSION`. -- Sets `version` (for example `4.7`) and `release` (for example `4.7.6`) in **user_guide_src/source/conf.py**. -- Sets the release date in **user_guide_src/source/changelogs/v$VERSION.rst** (format `Release Date: January 31, 2021`). +- Sets the minor version (for example `4.7`) in the title `## Version 4.7` of **docs/changelogs/index.md**. +- Sets the release date in **docs/changelogs/v$VERSION.md** (format `Release Date: January 31, 2021`). - Updates the title and version in **phpdoc.dist.xml**. - Updates **admin/starter/builds**: `LATEST_RELEASE` becomes `^` and `NEXT_MINOR` becomes `^-dev`. - Commits everything, including your uncommitted edits to tracked files, as `Prep for $VERSION release`. @@ -490,8 +490,9 @@ php admin/create-new-changelog.php $VERSION $NEXT_VERSION The script does the following: -- Creates **user_guide_src/source/changelogs/v$NEXT_VERSION.rst** from **admin/next-changelog-patch.rst** and adds it to **index.rst**. -- Creates the upgrade guide stub from **admin/next-upgrading-guide.rst** and adds it to **upgrading.rst**. +- Creates **docs/changelogs/v$NEXT_VERSION.md** from **admin/next-changelog-patch.md** and adds it to **index.md**. +- Creates the upgrade guide stub from **admin/next-upgrading-guide.md** and adds it to **upgrading.md**. +- Adds both pages to **docs/.nav.yml**. - Sets `CI_VERSION` to `$NEXT_VERSION-dev`. - Creates the branch `docs-changelog-$NEXT_VERSION` from `develop` and commits. diff --git a/admin/create-new-changelog.php b/admin/create-new-changelog.php index 32ac5fdaa52c..2962aaf1a35d 100644 --- a/admin/create-new-changelog.php +++ b/admin/create-new-changelog.php @@ -38,13 +38,13 @@ function replace_file_content(string $path, string $pattern, string $replace): v } // Copy changelog -$newChangelog = "./user_guide_src/source/changelogs/v{$newVersion}.rst"; -$changelogIndex = './user_guide_src/source/changelogs/index.rst'; +$newChangelog = "./docs/changelogs/v{$newVersion}.md"; +$changelogIndex = './docs/changelogs/index.md'; if ($isMinorUpdate) { - copy('./admin/next-changelog-minor.rst', $newChangelog); + copy('./admin/next-changelog-minor.md', $newChangelog); } else { - copy('./admin/next-changelog-patch.rst', $newChangelog); + copy('./admin/next-changelog-patch.md', $newChangelog); } // Replace version in CodeIgniter.php to {version}-dev. @@ -54,46 +54,42 @@ function replace_file_content(string $path, string $pattern, string $replace): v "public const CI_VERSION = '{$newVersion}-dev';", ); -// Add changelog to index.rst. +// Add changelog to the index, newest first. replace_file_content( $changelogIndex, - '/\.\. toctree::\n :titlesonly:\n/u', - ".. toctree::\n :titlesonly:\n\n v{$newVersion}", + '/^(See all the changes\.\n\n)/mu', + "\$1- [Version {$newVersion}](v{$newVersion}.md)\n", ); // Replace {version} -$underline = str_repeat('#', mb_strlen("Version {$newVersion}")); -replace_file_content( - $newChangelog, - '/#################\nVersion {version}\n#################/u', - "{$underline}\nVersion {$newVersion}\n{$underline}", -); replace_file_content($newChangelog, '/{version}/u', $newVersion); // Copy upgrading $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', $newUpgrading); +$newUpgrading = "./docs/installation/upgrade_{$versionWithoutDots}.md"; +$upgradingIndex = './docs/installation/upgrading.md'; +copy('./admin/next-upgrading-guide.md', $newUpgrading); -// Add upgrading to upgrading.rst. +// Add upgrading to the index, right after the backward compatibility notes. replace_file_content( $upgradingIndex, - '/ backward_compatibility_notes\n/u', - " backward_compatibility_notes\n\n upgrade_{$versionWithoutDots}", + '/^(- \[Backward Compatibility Notes\]\(backward_compatibility_notes\.md\)\n)/mu', + "\$1- [Upgrading from {$currentVersion} to {$newVersion}](upgrade_{$versionWithoutDots}.md)\n", ); // Replace {version} -$underline = str_repeat('#', mb_strlen("Upgrading from {$currentVersion} to {$newVersion}")); replace_file_content( $newUpgrading, - '/##############################\nUpgrading from {version} to {version}\n##############################/u', - "{$underline}\nUpgrading from {$currentVersion} to {$newVersion}\n{$underline}", + '/^# Upgrading from {version} to {version}$/mu', + "# Upgrading from {$currentVersion} to {$newVersion}", ); +// Add both pages to the nav. +system('php ./admin/update-docs-nav.php'); + if (! in_array('--dry-run', $argv, true)) { system('git add ./system/CodeIgniter.php'); system("git add {$newChangelog} {$changelogIndex}"); - system("git add {$newUpgrading} {$upgradingIndex}"); + system("git add {$newUpgrading} {$upgradingIndex} ./docs/.nav.yml"); system("git commit -m \"docs: add changelog and upgrade for v{$newVersion}\""); } diff --git a/admin/generate-changelog.php b/admin/generate-changelog.php index 521d2744669e..ffb164516ebb 100644 --- a/admin/generate-changelog.php +++ b/admin/generate-changelog.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/** - * Generates the CHANGELOG.md entry for a new release from GitHub's - * auto-generated release notes and prepends it to CHANGELOG.md. - * - * Requires the authenticated GitHub CLI (`gh`). - * - * Usage: php admin/generate-changelog.php [--dry-run] - */ chdir(__DIR__ . '/..'); $args = array_slice($argv, 1); @@ -99,13 +91,13 @@ // Extracts the security fixes from the SECURITY section of the detailed // changelog, if any. These come from security advisories, not from PRs, so // they are not part of the generated release notes. -$rstPath = "./user_guide_src/source/changelogs/v{$version}.rst"; -$security = ''; +$detailedPath = "./docs/changelogs/v{$version}.md"; +$security = ''; -if (is_file($rstPath)) { - $rst = file_get_contents($rstPath); +if (is_file($detailedPath)) { + $detailed = file_get_contents($detailedPath); - if (preg_match('/^\*+\nSECURITY\n\*+\n(.*?)(?=^\*+$|\z)/msu', $rst, $matches) === 1) { + if (preg_match('/^## SECURITY\n(.*?)(?=^## |\z)/msu', $detailed, $matches) === 1) { $security = trim($matches[1], "\n"); $security = preg_replace('/^- /mu', '* ', $security); } diff --git a/admin/next-changelog-minor.md b/admin/next-changelog-minor.md new file mode 100644 index 000000000000..e0c58b326153 --- /dev/null +++ b/admin/next-changelog-minor.md @@ -0,0 +1,49 @@ +# Version {version} + +Release Date: Unreleased + +**{version} release of CodeIgniter4** + +## Highlights + +- TBD + +## BREAKING + +### Behavior Changes + +### Interface Changes + +### Method Signature Changes + +## Enhancements + +### Commands + +### Testing + +### Database + +#### Query Builder + +#### Forge + +#### Others + +### Model + +### Libraries + +### Helpers and Functions + +### Others + +## Message Changes + +## Changes + +## Deprecations + +## Bugs Fixed + +See the repo's [CHANGELOG.md](https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md) for a complete list of bugs fixed. diff --git a/admin/next-changelog-patch.md b/admin/next-changelog-patch.md new file mode 100644 index 000000000000..04414d7cc513 --- /dev/null +++ b/admin/next-changelog-patch.md @@ -0,0 +1,17 @@ +# Version {version} + +Release Date: Unreleased + +**{version} release of CodeIgniter4** + +## BREAKING + +## Message Changes + +## Changes + +## Deprecations + +## Bugs Fixed + +See the repo's [CHANGELOG.md](https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md) for a complete list of bugs fixed. diff --git a/admin/next-upgrading-guide.md b/admin/next-upgrading-guide.md new file mode 100644 index 000000000000..e7c0e130b8d0 --- /dev/null +++ b/admin/next-upgrading-guide.md @@ -0,0 +1,34 @@ +# Upgrading from {version} to {version} + +Please refer to the upgrade instructions corresponding to your installation method. + +- [Composer Installation App Starter Upgrading](installing_composer.md#upgrading) +- [Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading](installing_composer.md#upgrading_1) +- [Manual Installation Upgrading](installing_manual.md#upgrading) + +## Mandatory File Changes + +## Breaking Changes + +## Breaking Enhancements + +## Project Files + +Some files in the **project space** (root, app, public, writable) received updates. Due to these files being outside of the **system** scope they will not be changed without your intervention. + +!!! note "Note" + There are some third-party CodeIgniter modules available to assist with merging changes to the project space: [Explore on Packagist](https://packagist.org/explore/?query=codeigniter4%20updates). + +### Content Changes + +The following files received significant changes (including deprecations or visual adjustments) and it is recommended that you merge the updated versions with your application: + +#### Config + +- @TODO + +### All Changes + +This is a list of all files in the **project space** that received changes; many will be simple comments or formatting that have no effect on the runtime: + +- @TODO diff --git a/admin/prepare-release.php b/admin/prepare-release.php index d68ae249b604..fda617cb7b09 100644 --- a/admin/prepare-release.php +++ b/admin/prepare-release.php @@ -39,16 +39,11 @@ function replace_file_content(string $path, string $pattern, string $replace): v "public const CI_VERSION = '{$version}';", ); -// Updates version number in "conf.py". +// Updates the minor version in the title of the changelog index. replace_file_content( - './user_guide_src/source/conf.py', - '/^version = \'.*?\'/mu', - "version = '{$minor}'", -); -replace_file_content( - './user_guide_src/source/conf.py', - '/^release = \'.*?\'/mu', - "release = '{$version}'", + './docs/changelogs/index.md', + '/^## Version .*/mu', + "## Version {$minor}", ); // Updates version number in "phpdoc.dist.xml". @@ -66,7 +61,7 @@ function replace_file_content(string $path, string $pattern, string $replace): v // Updates release date in changelogs. $date = date('F j, Y'); replace_file_content( - "./user_guide_src/source/changelogs/v{$version}.rst", + "./docs/changelogs/v{$version}.md", '/^Release Date: .*/mu', "Release Date: {$date}", ); diff --git a/admin/update-upgrade-guide.php b/admin/update-upgrade-guide.php index 19d42bed64d9..d0959bf53b5e 100644 --- a/admin/update-upgrade-guide.php +++ b/admin/update-upgrade-guide.php @@ -58,7 +58,7 @@ function bullets(array $items): string $version = $params[0]; $dryRun = in_array('--dry-run', $options, true); -$upgradePath = sprintf('./user_guide_src/source/installation/upgrade_%s.rst', str_replace('.', '', $version)); +$upgradePath = sprintf('./docs/installation/upgrade_%s.md', str_replace('.', '', $version)); if (! is_file($upgradePath)) { echo sprintf("%s is not found. Run \"php admin/create-new-changelog.php\" first.\n", $upgradePath); @@ -83,11 +83,13 @@ function bullets(array $items): string ':!admin/', ':!changelogs/', ':!contributing/', + ':!docs/', ':!system/', ':!tests/', ':!user_guide_src/', ':!utils/', ':!.*', + ':!mkdocs.yml', ':!*.md', ':!*.json', ':!*.xml', @@ -160,21 +162,21 @@ function bullets(array $items): string : bullets($allChanges); if ($dryRun) { - echo "\nConfig\n------\n\n{$configContent}\n"; - echo "\nAll Changes\n===========\n\n{$allChangesContent}\n"; + echo "\n#### Config\n\n{$configContent}\n"; + echo "\n### All Changes\n\n{$allChangesContent}\n"; exit(0); } -$rst = file_get_contents($upgradePath); +$markdown = file_get_contents($upgradePath); // Fills the "Config" section, only if it still has the placeholder. Entries // may have been added ahead of time for changes that need discussion, and // those must not be overwritten. -if (preg_match('/^Config\n------\n\n(.*?)(?=^[^\s-][^\n]*\n(?:-+|=+)$|\z)/msu', $rst, $matches) !== 1) { +if (preg_match('/^#### Config\n\n(.*?)(?=^#{1,4} |\z)/msu', $markdown, $matches) !== 1) { echo "Could not find the \"Config\" section in {$upgradePath}. Update it manually.\n"; } elseif (trim($matches[1]) === '- @TODO') { - $rst = str_replace("Config\n------\n\n{$matches[1]}", "Config\n------\n\n{$configContent}\n\n", $rst); + $markdown = str_replace("#### Config\n\n{$matches[1]}", "#### Config\n\n{$configContent}\n\n", $markdown); echo "The \"Config\" section was updated. Add notes to each entry as needed.\n"; } else { @@ -195,8 +197,8 @@ function bullets(array $items): string } // Fills the "All Changes" section, replacing any previous list. -$pattern = '/^(All Changes\n===========\n\n.*?:\n\n)(.*?)(?=^\*+$|\z)/msu'; -$updated = preg_replace($pattern, "\$1{$allChangesContent}\n", $rst, 1, $count); +$pattern = '/^(### All Changes\n\n.*?:\n\n)(.*?)(?=^#{1,3} |\z)/msu'; +$updated = preg_replace($pattern, "\$1{$allChangesContent}\n", $markdown, 1, $count); if ($count !== 1) { echo "Could not find the \"All Changes\" section in {$upgradePath}. Update it manually.\n"; diff --git a/tests/system/AutoReview/CreateNewChangelogTest.php b/tests/system/AutoReview/CreateNewChangelogTest.php index 6b22fe0d9012..1503bd7e00a0 100644 --- a/tests/system/AutoReview/CreateNewChangelogTest.php +++ b/tests/system/AutoReview/CreateNewChangelogTest.php @@ -51,12 +51,13 @@ public function testCreateNewChangelog(string $mode): void $currentVersion = $this->currentVersion; $newVersion = $this->incrementVersion($currentVersion, $mode); $versionWithoutDots = str_replace('.', '', $newVersion); - $changelogPath = "./user_guide_src/source/changelogs/v{$newVersion}.rst"; - $upgradePath = "./user_guide_src/source/installation/upgrade_{$versionWithoutDots}.rst"; + $changelogPath = "./docs/changelogs/v{$newVersion}.md"; + $upgradePath = "./docs/installation/upgrade_{$versionWithoutDots}.md"; $trackedPathArgs = implode(' ', array_map(escapeshellarg(...), [ './system/CodeIgniter.php', - './user_guide_src/source/changelogs/index.rst', - './user_guide_src/source/installation/upgrading.rst', + './docs/.nav.yml', + './docs/changelogs/index.md', + './docs/installation/upgrading.md', ])); $generatedPathArgs = implode(' ', array_map(escapeshellarg(...), [ $changelogPath, @@ -88,7 +89,7 @@ public function testCreateNewChangelog(string $mode): void $this->assertFileExists($changelogPath); $this->assertStringContainsString( - "Version {$newVersion}", + "# Version {$newVersion}\n", $this->getContents($changelogPath), ); $this->assertStringContainsString( @@ -96,18 +97,26 @@ public function testCreateNewChangelog(string $mode): void $this->getContents($changelogPath), ); $this->assertStringContainsString( - $newVersion, - $this->getContents('./user_guide_src/source/changelogs/index.rst'), + "- [Version {$newVersion}](v{$newVersion}.md)", + $this->getContents('./docs/changelogs/index.md'), + ); + $this->assertStringContainsString( + "changelogs/v{$newVersion}.md", + $this->getContents('./docs/.nav.yml'), ); $this->assertFileExists($upgradePath); $this->assertStringContainsString( - "Upgrading from {$currentVersion} to {$newVersion}", + "# Upgrading from {$currentVersion} to {$newVersion}\n", $this->getContents($upgradePath), ); $this->assertStringContainsString( - "upgrade_{$versionWithoutDots}", - $this->getContents('./user_guide_src/source/installation/upgrading.rst'), + "(upgrade_{$versionWithoutDots}.md)", + $this->getContents('./docs/installation/upgrading.md'), + ); + $this->assertStringContainsString( + "installation/upgrade_{$versionWithoutDots}.md", + $this->getContents('./docs/.nav.yml'), ); } finally { exec("git restore -- {$trackedPathArgs}"); diff --git a/tests/system/AutoReview/GenerateChangelogTest.php b/tests/system/AutoReview/GenerateChangelogTest.php index cbb7e87e1e5c..fc3c6e2383fc 100644 --- a/tests/system/AutoReview/GenerateChangelogTest.php +++ b/tests/system/AutoReview/GenerateChangelogTest.php @@ -149,18 +149,18 @@ private function latestChangelogVersion(): string private function latestVersionWithSecuritySection(): ?string { $versions = []; - $paths = glob('./user_guide_src/source/changelogs/v*.rst'); + $paths = glob('./docs/changelogs/v*.md'); if ($paths === false) { return null; } foreach ($paths as $path) { - if (preg_match('/v(\d+\.\d+\.\d+)\.rst$/', $path, $matches) !== 1) { + if (preg_match('/v(\d+\.\d+\.\d+)\.md$/', $path, $matches) !== 1) { continue; } - if (str_contains((string) file_get_contents($path), "\nSECURITY\n")) { + if (str_contains((string) file_get_contents($path), "\n## SECURITY\n")) { $versions[] = $matches[1]; } } diff --git a/tests/system/AutoReview/UpdateUpgradeGuideTest.php b/tests/system/AutoReview/UpdateUpgradeGuideTest.php index bc7e2fc07cd3..97fb711bc648 100644 --- a/tests/system/AutoReview/UpdateUpgradeGuideTest.php +++ b/tests/system/AutoReview/UpdateUpgradeGuideTest.php @@ -44,7 +44,7 @@ protected function setUp(): void $this->nextVersion = sprintf('%d.%d.%d', $parts[0], $parts[1], ++$parts[2]); $this->upgradePath = sprintf( - './user_guide_src/source/installation/upgrade_%s.rst', + './docs/installation/upgrade_%s.md', str_replace('.', '', $this->nextVersion), ); } @@ -102,7 +102,7 @@ public function testUpdatesUpgradeGuide(): void $this->assertStringContainsString('"All Changes" section', $outputString); $contents = (string) file_get_contents($this->upgradePath); - $allChanges = substr($contents, (int) strpos($contents, "All Changes\n")); + $allChanges = substr($contents, (int) strpos($contents, "### All Changes\n")); $this->assertStringNotContainsString('- @TODO', $contents); $this->assertMatchesRegularExpression('/^- \S/m', $allChanges);