#!/usr/bin/env php * * SPDX-License-Identifier: GPL-3.0-or-later * * FILE INFORMATION * DEFGROUP: moko-platform.CLI * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/changelog_prune.php * BRIEF: Prune old CHANGELOG.md entries — keeps [Unreleased] + last N releases * * Usage: * php changelog_prune.php --path /repo --keep 5 * php changelog_prune.php --path /repo --keep 3 --dry-run */ declare(strict_types=1); $path = '.'; $keep = 5; $dryRun = false; foreach ($argv as $i => $arg) { if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1]; if ($arg === '--keep' && isset($argv[$i + 1])) $keep = (int)$argv[$i + 1]; if ($arg === '--dry-run') $dryRun = true; if ($arg === '--help') { echo "changelog_prune — Keep [Unreleased] + last N versioned entries\n\n"; echo "Usage: php changelog_prune.php --path . --keep 5 [--dry-run]\n\n"; echo "Options:\n"; echo " --path Repository path (default: .)\n"; echo " --keep Number of versioned releases to keep (default: 5)\n"; echo " --dry-run Preview without writing\n"; exit(0); } } $changelog = realpath($path) . '/CHANGELOG.md'; if (!file_exists($changelog)) { fwrite(STDERR, "No CHANGELOG.md found at {$path}\n"); exit(1); } $content = file_get_contents($changelog); $lines = explode("\n", $content); // Split into sections by ## headings $sections = []; $current = []; $currentHeading = null; foreach ($lines as $line) { if (preg_match('/^## /', $line)) { if ($currentHeading !== null) { $sections[] = ['heading' => $currentHeading, 'lines' => $current]; } $currentHeading = $line; $current = [$line]; } else { $current[] = $line; } } if ($currentHeading !== null) { $sections[] = ['heading' => $currentHeading, 'lines' => $current]; } // Find the header (everything before the first ## section) $header = []; $contentLines = explode("\n", $content); foreach ($contentLines as $line) { if (preg_match('/^## /', $line)) { break; } $header[] = $line; } // Separate [Unreleased] from versioned sections $unreleased = null; $versioned = []; foreach ($sections as $section) { if (preg_match('/\[Unreleased\]/i', $section['heading'])) { $unreleased = $section; } else { $versioned[] = $section; } } $totalVersioned = count($versioned); $pruned = $totalVersioned - $keep; if ($pruned <= 0) { echo "CHANGELOG has {$totalVersioned} versioned entries — nothing to prune (keeping {$keep})\n"; exit(0); } // Keep only the first N versioned sections $keptVersioned = array_slice($versioned, 0, $keep); $droppedVersioned = array_slice($versioned, $keep); // Report echo "CHANGELOG: {$totalVersioned} versioned entries found\n"; echo " Keeping: {$keep} most recent\n"; echo " Pruning: {$pruned} old entries\n"; foreach ($droppedVersioned as $section) { $heading = trim($section['heading']); echo " - {$heading}\n"; } if ($dryRun) { echo "\n(dry-run) No changes written\n"; exit(0); } // Rebuild the file $output = implode("\n", $header); if ($unreleased !== null) { $output .= implode("\n", $unreleased['lines']) . "\n"; } foreach ($keptVersioned as $section) { $output .= implode("\n", $section['lines']) . "\n"; } // Clean up excessive blank lines at end $output = rtrim($output) . "\n"; file_put_contents($changelog, $output); echo "\nCHANGELOG pruned: removed {$pruned} old entries\n"; exit(0);