diff --git a/.gitattributes b/.gitattributes index 9645b77e9..0af3f7329 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,4 +8,5 @@ docker-compose.yml export-ignore phpstan.neon export-ignore phpunit.xml export-ignore +bin/ export-ignore tests/ export-ignore diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 99adc703b..3da56d980 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,6 +4,7 @@ on: paths: - .php-cs-fixer.dist.php - autoload.php + - bin/** - lib/** - data/** - tests/** @@ -13,6 +14,7 @@ on: paths: - .php-cs-fixer.dist.php - autoload.php + - bin/** - lib/** - data/** - tests/** diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..df62275f6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +on: + push: + tags: + - "**" + +name: Release + +jobs: + release: + name: Release + + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + extensions: none + tools: none + + - name: Determine tag + run: echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + + - name: Parse ChangeLog + run: bin/extract-release-notes.php ${{ env.RELEASE_TAG }} > release-notes.md + + - name: Create release + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ env.RELEASE_TAG }} + name: Symfony1 ${{ env.RELEASE_TAG }} + bodyFile: release-notes.md diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index fccc2b3f9..000f91b81 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -3,6 +3,7 @@ $finder = PhpCsFixer\Finder::create() ->ignoreVCSIgnored(true) ->in(__DIR__.'/lib') + ->in(__DIR__.'/bin') ->in(__DIR__.'/data/bin') ->in(__DIR__.'/test') ->append([__FILE__]) diff --git a/bin/extract-release-notes.php b/bin/extract-release-notes.php new file mode 100755 index 000000000..d68cbee49 --- /dev/null +++ b/bin/extract-release-notes.php @@ -0,0 +1,61 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (2 !== $_SERVER['argc']) { + echo sprintf('Usage: %s version', basename(__FILE__)).PHP_EOL; + exit(1); +} + +$version = $_SERVER['argv'][1]; +$rawVersion = str_starts_with($version, 'v') ? substr($version, 1) : $version; + +$changelogFilePath = __DIR__.'/../CHANGELOG.md'; + +if (!is_file($changelogFilePath) || !is_readable($changelogFilePath)) { + echo 'Changelog file cannot read.'.PHP_EOL; + exit(1); +} + +$buffer = ''; +$state = 'not_release_note'; + +foreach (file($changelogFilePath) as $line) { + if (str_contains($line, 'Version '.$rawVersion)) { + $state = 'note_header'; + continue; + } + + if ('note_header' === $state) { + $state = 'note'; + continue; + } + + if ('note' === $state && str_contains($line, 'Version ')) { + break; + } + + if ('note' === $state) { + $buffer .= $line; + } +} + +$buffer = trim($buffer); + +if ('' === $buffer) { + echo 'Release note not found for the specified version.'.PHP_EOL; + exit(1); +} + +echo $buffer.PHP_EOL; +exit(0); diff --git a/bin/release.php b/bin/release.php new file mode 100755 index 000000000..e877f8442 --- /dev/null +++ b/bin/release.php @@ -0,0 +1,146 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (PHP_VERSION_ID < 80300) { + echo 'PHP 8.3 required'.PHP_EOL; + exit(1); +} + +if (!isset($argv[1])) { + throw new InvalidArgumentException('You must specify the version: v1.x.x or next.'); +} + +$version = $argv[1]; + +exec('git tag -l', $tags, $resultCode); + +if ($resultCode > 0 || 0 === count($tags)) { + throw new RuntimeException('Reading tag failed.'); +} + +$latestVersionNumber = $tags[0]; +foreach ($tags as $tag) { + if (version_compare($latestVersionNumber, $tag) < 0) { + $latestVersionNumber = $tag; + } +} + +if ('next' !== $version) { + if (!preg_match('/^v1\.([5-9]|\d{2,})\.\d+$/', $version)) { + throw new InvalidArgumentException(sprintf('The format of the specified version number is incorrect: "%s"', $version)); + } + + if (in_array($version, $tags)) { + throw new InvalidArgumentException(sprintf('The specified version number already exists: "%s"', $version)); + } + + [$latestMajorPart, $latestMinorPart, $latestPatchPart] = explode('.', $latestVersionNumber); + [$versionMajorPart, $versionMinorPart, $versionPatchPart] = explode('.', $version); + + // This cannot be due to regexp. Just double check. + if ($latestMajorPart !== $versionMajorPart) { + throw new InvalidArgumentException(sprintf('The specified version number can\'t change major: "%s"', $version)); + } + + // changed minor or patch + if ($latestMinorPart !== $versionMinorPart) { + if ('0' !== $versionPatchPart) { + throw new InvalidArgumentException(sprintf('The specified version number should be: "%s.%s.0"', $versionMajorPart, $versionMinorPart)); + } + } elseif ($latestPatchPart !== $versionPatchPart) { + $latestPatchPartInt = (int) $latestPatchPart; + $versionPatchPartInt = (int) $versionPatchPart; + + $nextPatchPartInt = $latestPatchPartInt + 1; + + if ($nextPatchPartInt !== $versionPatchPartInt) { + throw new InvalidArgumentException(sprintf('Don\'t skip patch version. The specified version number should be: "%s.%s.%d"', $versionMajorPart, $versionMinorPart, $nextPatchPartInt)); + } + } +} else { + [$latestMajorPart, $latestMinorPart, $latestPatchPart] = explode('.', $latestVersionNumber); + $nextPatchPart = (int) $latestPatchPart + 1; + $version = sprintf('%s.%s.%d', $latestMajorPart, $latestMinorPart, $nextPatchPart); +} + +$rawVersion = substr($version, 1); + +echo sprintf("Prepare symfony version \"%s\".\n", $rawVersion); + +/** + * prepare sfCoreAutoload class. + */ +$file = __DIR__.'/../lib/autoload/sfCoreAutoload.class.php'; +$content = file_get_contents($file); + +$content = preg_replace('/^define\(.*SYMFONY_VERSION.*$/m', 'define(\'SYMFONY_VERSION\', \''.$rawVersion.'\');', $content, -1, $count); + +if (1 !== $count) { + throw new RuntimeException('Preparing sfCoreAutoload failed, SYMFONY_VERSION constant not found.'); +} + +file_put_contents($file, $content); + +/** + * prepare CHANGELOG.md. + */ +$file = __DIR__.'/../CHANGELOG.md'; +$content = file_get_contents($file); + +$nextVersionHeader = <<<'EOL' +xx/xx/xxxx: Version 1.5.xx +-------------------------- + + + + +EOL; + +$changelogHeader = sprintf('%s: Version %s', date('d/m/Y'), $rawVersion); +$content = preg_replace('/^xx\/xx\/xxxx.*$/m', $nextVersionHeader.$changelogHeader, $content, -1, $count); + +if (1 !== $count) { + throw new RuntimeException('Preparing CHANGELOG.md failed. Template line not found.'); +} + +file_put_contents($file, $content); + +/* + * content prepare end + */ + +echo "Please check the changes before commit and tagging.\n"; + +passthru('git diff'); + +echo "Is everything ok? (y/N)\n"; + +$answer = readline(); + +if ('y' !== strtolower($answer)) { + echo "Revert changes.\n"; + exec('git checkout lib/autoload/sfCoreAutoload.class.php'); + exec('git checkout CHANGELOG.md'); + echo "Stopped.\n"; + exit(1); +} + +chdir(__DIR__.'/..'); +exec('git add lib/autoload/sfCoreAutoload.class.php'); +exec('git add CHANGELOG.md'); +exec('git commit -m "Prepare release '.$rawVersion.'"'); +exec('git tag -a '.$version.' -m "Release '.$rawVersion.'"'); +exec('git push origin '.$version); + +exit(0); diff --git a/composer.json b/composer.json index 90a612f1c..ce6222d12 100755 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "friendsofsymfony1/swiftmailer": "^5.4.13 || ^6.2.5" }, "require-dev": { - "psr/log": "*" + "psr/log": "*", + "ext-readline": "*" }, "autoload": { "files": ["autoload.php"] diff --git a/data/bin/changelog.php b/data/bin/changelog.php deleted file mode 100644 index 6a3218a4b..000000000 --- a/data/bin/changelog.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * Outputs formatted Subversion log entries. - * - * Usage: php data/bin/changelog.php -r12345:67890 /branches/1.3 - * - * @author Fabien Potencier - * - * @version SVN: $Id$ - */ -require_once __DIR__.'/../../lib/task/sfFilesystem.class.php'; - -if (!isset($argv[1])) { - echo "You must provide a revision range (-r123:456)\n"; - - exit(1); -} - -if (!isset($argv[2])) { - echo "You must provide a repository path (/branches/1.4)\n"; - - exit(1); -} - -$filesystem = new sfFilesystem(); - -list($out, $err) = $filesystem->execute('svn info --xml'); -$info = new SimpleXMLElement($out); - -list($out, $err) = $filesystem->execute(vsprintf('svn log %s --xml %s', array_map('escapeshellarg', [ - $argv[1], - (string) $info->entry->repository->root.$argv[2], -]))); -$log = new SimpleXMLElement($out); - -foreach ($log->logentry as $logentry) { - echo sprintf(' * [%d] %s', $logentry['revision'], trim(preg_replace('/\s*\[[\d\., ]+\]\s*/', '', (string) $logentry->msg))); - echo PHP_EOL; -} diff --git a/data/bin/release.php b/data/bin/release.php deleted file mode 100644 index ad652a3bf..000000000 --- a/data/bin/release.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * Release script. - * - * Usage: php data/bin/release.php 1.3.0 stable - * - * @author Fabien Potencier - * - * @version SVN: $Id$ - */ -require_once __DIR__.'/../../lib/exception/sfException.class.php'; - -require_once __DIR__.'/../../lib/task/sfFilesystem.class.php'; - -require_once __DIR__.'/../../lib/util/sfFinder.class.php'; - -require_once __DIR__.'/../../lib/vendor/lime/lime.php'; - -if (!isset($argv[1])) { - throw new Exception('You must provide version prefix.'); -} - -if (!isset($argv[2])) { - throw new Exception('You must provide stability status (alpha/beta/stable).'); -} - -$stability = $argv[2]; - -$filesystem = new sfFilesystem(); - -if (('beta' == $stability || 'alpha' == $stability) && count(explode('.', $argv[1])) < 2) { - $version_prefix = $argv[1]; - - list($result) = $filesystem->execute('svn status -u '.getcwd()); - if (preg_match('/Status against revision\:\s+(\d+)\s*$/im', $result, $match)) { - $version = $match[1]; - } - - if (!isset($version)) { - throw new Exception('Unable to find last SVN revision.'); - } - - // make a PEAR compatible version - $version = $version_prefix.'.'.$version; -} else { - $version = $argv[1]; -} - -echo sprintf("Releasing symfony version \"%s\".\n", $version); - -// tests -list($result) = $filesystem->execute('php data/bin/symfony symfony:test'); - -if (0 != $result) { - throw new Exception('Some tests failed. Release process aborted!'); -} - -if (is_file('package.xml')) { - $filesystem->remove(getcwd().DIRECTORY_SEPARATOR.'package.xml'); -} - -$filesystem->copy(getcwd().'/package.xml.tmpl', getcwd().'/package.xml'); - -// add class files -$finder = sfFinder::type('file')->relative(); -$xml_classes = ''; -$dirs = ['lib' => 'php', 'data' => 'data']; -foreach ($dirs as $dir => $role) { - $class_files = $finder->in($dir); - foreach ($class_files as $file) { - $xml_classes .= ''."\n"; - } -} - -// replace tokens -$filesystem->replaceTokens(getcwd().DIRECTORY_SEPARATOR.'package.xml', '##', '##', [ - 'SYMFONY_VERSION' => $version, - 'CURRENT_DATE' => date('Y-m-d'), - 'CLASS_FILES' => $xml_classes, - 'STABILITY' => $stability, -]); - -list($results) = $filesystem->execute('pear package'); -echo $results; - -$filesystem->remove(getcwd().DIRECTORY_SEPARATOR.'package.xml'); - -exit(0);