From a5abab9ab0bbbcc234aec954c3e7f9cec5da9280 Mon Sep 17 00:00:00 2001 From: Paul Sealock Date: Thu, 11 Nov 2021 16:28:10 +1300 Subject: [PATCH] Add Jetpack Changelogger --- packages/js/api-core-tests/composer.json | 31 ++ packages/js/api/changelog/.gitkeep | 0 packages/js/api/composer.json | 31 ++ packages/js/e2e-core-tests/changelog/.gitkeep | 0 packages/js/e2e-core-tests/composer.json | 31 ++ .../js/e2e-environment/changelog/.gitkeep | 0 packages/js/e2e-environment/composer.json | 31 ++ packages/js/e2e-utils/changelog/.gitkeep | 0 packages/js/e2e-utils/composer.json | 31 ++ plugins/woocommerce/NEXT_CHANGELOG.md | 0 plugins/woocommerce/changelog/.gitkeep | 0 plugins/woocommerce/composer.json | 257 +++++++++-------- tools/changelogger/Formatter.php | 273 ++++++++++++++++++ tools/changelogger/PackageFormatter.php | 27 ++ tools/changelogger/PluginFormatter.php | 27 ++ 15 files changed, 620 insertions(+), 119 deletions(-) create mode 100644 packages/js/api-core-tests/composer.json create mode 100644 packages/js/api/changelog/.gitkeep create mode 100644 packages/js/api/composer.json create mode 100644 packages/js/e2e-core-tests/changelog/.gitkeep create mode 100644 packages/js/e2e-core-tests/composer.json create mode 100644 packages/js/e2e-environment/changelog/.gitkeep create mode 100644 packages/js/e2e-environment/composer.json create mode 100644 packages/js/e2e-utils/changelog/.gitkeep create mode 100644 packages/js/e2e-utils/composer.json create mode 100644 plugins/woocommerce/NEXT_CHANGELOG.md create mode 100644 plugins/woocommerce/changelog/.gitkeep create mode 100644 tools/changelogger/Formatter.php create mode 100644 tools/changelogger/PackageFormatter.php create mode 100644 tools/changelogger/PluginFormatter.php diff --git a/packages/js/api-core-tests/composer.json b/packages/js/api-core-tests/composer.json new file mode 100644 index 00000000000..9d8fefc962a --- /dev/null +++ b/packages/js/api-core-tests/composer.json @@ -0,0 +1,31 @@ +{ + "name": "woocommerce/api-core-tests", + "description": "WooCommerce API core test", + "type": "library", + "license": "GPL-3.0-or-later", + "minimum-stability": "dev", + "require-dev": { + "automattic/jetpack-changelogger": "^1.2" + }, + "extra": { + "changelogger": { + "formatter": { + "class": "PackageFormatter" + }, + "types": [ + "Fix", + "Add", + "Update", + "Dev", + "Tweak", + "Performance", + "Enhancement" + ] + } + }, + "autoload": { + "files": [ + "../../../tools/changelogger/PackageFormatter.php" + ] + } +} diff --git a/packages/js/api/changelog/.gitkeep b/packages/js/api/changelog/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js/api/composer.json b/packages/js/api/composer.json new file mode 100644 index 00000000000..3597f78b208 --- /dev/null +++ b/packages/js/api/composer.json @@ -0,0 +1,31 @@ +{ + "name": "woocommerce/api", + "description": "WooCommerce API", + "type": "library", + "license": "GPL-3.0-or-later", + "minimum-stability": "dev", + "require-dev": { + "automattic/jetpack-changelogger": "^1.2" + }, + "extra": { + "changelogger": { + "formatter": { + "class": "PackageFormatter" + }, + "types": [ + "Fix", + "Add", + "Update", + "Dev", + "Tweak", + "Performance", + "Enhancement" + ] + } + }, + "autoload": { + "files": [ + "../../../tools/changelogger/PackageFormatter.php" + ] + } +} diff --git a/packages/js/e2e-core-tests/changelog/.gitkeep b/packages/js/e2e-core-tests/changelog/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js/e2e-core-tests/composer.json b/packages/js/e2e-core-tests/composer.json new file mode 100644 index 00000000000..100cd41bdbc --- /dev/null +++ b/packages/js/e2e-core-tests/composer.json @@ -0,0 +1,31 @@ +{ + "name": "woocommerce/e2e-core-tests", + "description": "WooCommerce end to end core tests", + "type": "library", + "license": "GPL-3.0-or-later", + "minimum-stability": "dev", + "require-dev": { + "automattic/jetpack-changelogger": "^1.2" + }, + "extra": { + "changelogger": { + "formatter": { + "class": "PackageFormatter" + }, + "types": [ + "Fix", + "Add", + "Update", + "Dev", + "Tweak", + "Performance", + "Enhancement" + ] + } + }, + "autoload": { + "files": [ + "../../../tools/changelogger/PackageFormatter.php" + ] + } +} diff --git a/packages/js/e2e-environment/changelog/.gitkeep b/packages/js/e2e-environment/changelog/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js/e2e-environment/composer.json b/packages/js/e2e-environment/composer.json new file mode 100644 index 00000000000..0287bd8e515 --- /dev/null +++ b/packages/js/e2e-environment/composer.json @@ -0,0 +1,31 @@ +{ + "name": "woocommerce/e2e-environment", + "description": "WooCommerce end to end testing environment", + "type": "library", + "license": "GPL-3.0-or-later", + "minimum-stability": "dev", + "require-dev": { + "automattic/jetpack-changelogger": "^1.2" + }, + "extra": { + "changelogger": { + "formatter": { + "class": "PackageFormatter" + }, + "types": [ + "Fix", + "Add", + "Update", + "Dev", + "Tweak", + "Performance", + "Enhancement" + ] + } + }, + "autoload": { + "files": [ + "../../../tools/changelogger/PackageFormatter.php" + ] + } +} diff --git a/packages/js/e2e-utils/changelog/.gitkeep b/packages/js/e2e-utils/changelog/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js/e2e-utils/composer.json b/packages/js/e2e-utils/composer.json new file mode 100644 index 00000000000..8eb2e954b77 --- /dev/null +++ b/packages/js/e2e-utils/composer.json @@ -0,0 +1,31 @@ +{ + "name": "woocommerce/e2e-utiles", + "description": "WooCommerce end to end testing utilities", + "type": "library", + "license": "GPL-3.0-or-later", + "minimum-stability": "dev", + "require-dev": { + "automattic/jetpack-changelogger": "^1.2" + }, + "extra": { + "changelogger": { + "formatter": { + "class": "PackageFormatter" + }, + "types": [ + "Fix", + "Add", + "Update", + "Dev", + "Tweak", + "Performance", + "Enhancement" + ] + } + }, + "autoload": { + "files": [ + "../../../tools/changelogger/PackageFormatter.php" + ] + } +} diff --git a/plugins/woocommerce/NEXT_CHANGELOG.md b/plugins/woocommerce/NEXT_CHANGELOG.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/woocommerce/changelog/.gitkeep b/plugins/woocommerce/changelog/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/woocommerce/composer.json b/plugins/woocommerce/composer.json index acab8ed832c..78e9a879d43 100644 --- a/plugins/woocommerce/composer.json +++ b/plugins/woocommerce/composer.json @@ -1,121 +1,140 @@ { - "name": "woocommerce/woocommerce", - "description": "An eCommerce toolkit that helps you sell anything. Beautifully.", - "homepage": "https://woocommerce.com/", - "type": "wordpress-plugin", - "license": "GPL-3.0-or-later", - "prefer-stable": true, - "minimum-stability": "dev", - "repositories": [ - { - "type": "path", - "url": "lib" - } - ], - "require": { - "php": ">=7.0", - "automattic/jetpack-autoloader": "2.10.1", - "automattic/jetpack-constants": "1.5.1", - "composer/installers": "~1.7", - "maxmind-db/reader": "1.6.0", - "pelago/emogrifier": "3.1.0", - "psr/container": "1.0.0", - "woocommerce/action-scheduler": "3.3.0", - "woocommerce/woocommerce-admin": "2.8.0", - "woocommerce/woocommerce-blocks": "6.1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4", - "yoast/phpunit-polyfills": "^1.0" - }, - "config": { - "optimize-autoloader": true, - "platform": { - "php": "7.0" - }, - "preferred-install": { - "woocommerce/action-scheduler": "dist", - "woocommerce/woocommerce-rest-api": "dist", - "woocommerce/woocommerce-blocks": "dist" - }, - "sort-packages": true - }, - "autoload": { - "exclude-from-classmap": [ - "includes/legacy", - "includes/libraries" - ], - "classmap": [ - "includes/rest-api" - ], - "psr-4": { - "Automattic\\WooCommerce\\": "src/", - "Automattic\\WooCommerce\\Vendor\\": "lib/packages/" - }, - "psr-0": { - "Automattic\\WooCommerce\\Vendor\\": "lib/packages/" - } - }, - "autoload-dev": { - "psr-4": { - "Automattic\\WooCommerce\\Tests\\": "tests/php/src/", - "Automattic\\WooCommerce\\Testing\\Tools\\": "tests/Tools/" - }, - "classmap": [ - "tests/legacy/unit-tests/rest-api/Helpers" - ] - }, - "scripts": { - "post-install-cmd": [ - "@composer bin all install --ansi", - "sh ./bin/package-update.sh" - ], - "post-update-cmd": [ - "@composer bin all update --ansi", - "sh ./bin/package-update.sh" - ], - "test": [ - "phpunit" - ], - "phpcs": [ - "phpcs -s -p" - ], - "phpcs-pre-commit": [ - "phpcs -s -p -n" - ], - "phpcbf": [ - "phpcbf -p" - ], - "makepot-audit": [ - "wp --allow-root i18n make-pot . --exclude=\".github,.wordpress-org,bin,sample-data,node_modules,tests\" --slug=woocommerce" - ], - "makepot": [ - "@makepot-audit --skip-audit" - ], - "bin": [ - "echo 'bin not installed'" - ], - "build-lib": [ - "sh ./bin/build-lib.sh" - ] - }, - "extra": { - "installer-paths": { - "packages/{$name}": [ - "woocommerce/action-scheduler", - "woocommerce/woocommerce-blocks", - "woocommerce/woocommerce-admin" - ] - }, - "scripts-description": { - "test": "Run unit tests", - "phpcs": "Analyze code against the WordPress coding standards with PHP_CodeSniffer", - "phpcbf": "Fix coding standards warnings/errors automatically with PHP Code Beautifier", - "makepot-audit": "Generate i18n/languages/woocommerce.pot file and run audit", - "makepot": "Generate i18n/languages/woocommerce.pot file" - }, - "bamarni-bin": { - "target-directory": "bin/composer" - } - } + "name": "woocommerce/woocommerce", + "description": "An eCommerce toolkit that helps you sell anything. Beautifully.", + "homepage": "https://woocommerce.com/", + "type": "wordpress-plugin", + "license": "GPL-3.0-or-later", + "prefer-stable": true, + "minimum-stability": "dev", + "repositories": [ + { + "type": "path", + "url": "lib" + } + ], + "require": { + "php": ">=7.0", + "automattic/jetpack-autoloader": "2.10.1", + "automattic/jetpack-constants": "1.5.1", + "composer/installers": "~1.7", + "maxmind-db/reader": "1.6.0", + "pelago/emogrifier": "3.1.0", + "psr/container": "1.0.0", + "woocommerce/action-scheduler": "3.3.0", + "woocommerce/woocommerce-admin": "2.8.0", + "woocommerce/woocommerce-blocks": "6.1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4", + "yoast/phpunit-polyfills": "^1.0", + "automattic/jetpack-changelogger": "1.2" + }, + "config": { + "optimize-autoloader": true, + "platform": { + "php": "7.0" + }, + "preferred-install": { + "woocommerce/action-scheduler": "dist", + "woocommerce/woocommerce-rest-api": "dist", + "woocommerce/woocommerce-blocks": "dist" + }, + "sort-packages": true + }, + "autoload": { + "exclude-from-classmap": [ + "includes/legacy", + "includes/libraries" + ], + "classmap": [ + "includes/rest-api" + ], + "psr-4": { + "Automattic\\WooCommerce\\": "src/", + "Automattic\\WooCommerce\\Vendor\\": "lib/packages/" + }, + "psr-0": { + "Automattic\\WooCommerce\\Vendor\\": "lib/packages/" + }, + "files": [ + "../../tools/changelogger/PluginFormatter.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Automattic\\WooCommerce\\Tests\\": "tests/php/src/", + "Automattic\\WooCommerce\\Testing\\Tools\\": "tests/Tools/" + }, + "classmap": [ + "tests/legacy/unit-tests/rest-api/Helpers" + ] + }, + "scripts": { + "post-install-cmd": [ + "@composer bin all install --ansi", + "sh ./bin/package-update.sh" + ], + "post-update-cmd": [ + "@composer bin all update --ansi", + "sh ./bin/package-update.sh" + ], + "test": [ + "phpunit" + ], + "phpcs": [ + "phpcs -s -p" + ], + "phpcs-pre-commit": [ + "phpcs -s -p -n" + ], + "phpcbf": [ + "phpcbf -p" + ], + "makepot-audit": [ + "wp --allow-root i18n make-pot . --exclude=\".github,.wordpress-org,bin,sample-data,node_modules,tests\" --slug=woocommerce" + ], + "makepot": [ + "@makepot-audit --skip-audit" + ], + "bin": [ + "echo 'bin not installed'" + ], + "build-lib": [ + "sh ./bin/build-lib.sh" + ] + }, + "extra": { + "installer-paths": { + "packages/{$name}": [ + "woocommerce/action-scheduler", + "woocommerce/woocommerce-blocks", + "woocommerce/woocommerce-admin" + ] + }, + "scripts-description": { + "test": "Run unit tests", + "phpcs": "Analyze code against the WordPress coding standards with PHP_CodeSniffer", + "phpcbf": "Fix coding standards warnings/errors automatically with PHP Code Beautifier", + "makepot-audit": "Generate i18n/languages/woocommerce.pot file and run audit", + "makepot": "Generate i18n/languages/woocommerce.pot file" + }, + "bamarni-bin": { + "target-directory": "bin/composer" + }, + "changelogger": { + "formatter": { + "class": "PluginFormatter" + }, + "types": [ + "Fix", + "Add", + "Update", + "Dev", + "Tweak", + "Performance", + "Enhancement" + ], + "versioning": "wordpress" + } + } } diff --git a/tools/changelogger/Formatter.php b/tools/changelogger/Formatter.php new file mode 100644 index 00000000000..7f2533ad5fb --- /dev/null +++ b/tools/changelogger/Formatter.php @@ -0,0 +1,273 @@ + 'https://www.npmjs.com/package/@woocommerce/components/v/', + 'plugins/woocommerce' => 'https://github.com/woocommerce/woocommerce/releases/tag/', + ); + + // Catpure anything past /woocommerce-monorepo in the current working directory. + preg_match( '/\/woocommerce-monorepo\/(.+)/', getcwd(), $path ); + + if ( ! count( $path ) ) { + throw new InvalidArgumentException( 'Invalid directory.' ); + } + + $release_url = $path_map[ $path[1] ]; + + if ( ! $release_url ) { + throw new InvalidArgumentException( 'Release URL not found.' ); + } + + return $release_url . $version; + } + + /** + * Modified version of parse() from KeepAChangelogParser. + * + * @param string $changelog Changelog contents. + * @return Changelog + * @throws InvalidArgumentException If the changelog data cannot be parsed. + */ + public function parse( $changelog ) { + $ret = new Changelog(); + + // Fix newlines and expand tabs. + $changelog = strtr( $changelog, array( "\r\n" => "\n" ) ); + $changelog = strtr( $changelog, array( "\r" => "\n" ) ); + while ( strpos( $changelog, "\t" ) !== false ) { + $changelog = preg_replace_callback( + '/^([^\t\n]*)\t/m', + function ( $m ) { + return $m[1] . str_repeat( ' ', 4 - ( mb_strlen( $m[1] ) % 4 ) ); + }, + $changelog + ); + } + + // Entries make up the rest of the document. + $entries = array(); + $entry_pattern = $this->entry_pattern; + preg_match_all( $entry_pattern, $changelog, $matches ); + + foreach ( $matches[0] as $section ) { + // Remove the epilogue, if it exists. + $section = str_replace( $this->epilogue, '', $section ); + + $heading_pattern = $this->heading_pattern; + $subentry_pattern = $this->subentry_pattern; + + // Parse the heading and create a ChangelogEntry for it. + preg_match( $heading_pattern, $section, $heading ); + + // Check if the heading may be a sub-heading. + preg_match( $subentry_pattern, $section, $subheading ); + $is_subentry = count( $subheading ) > 0; + + if ( ! count( $heading ) && ! count( $subheading ) ) { + throw new InvalidArgumentException( 'Invalid heading' ); + } + + $version = ''; + $timestamp = new DateTime( 'now', new DateTimeZone( 'UTC' ) ); + $entry_timestamp = new DateTime( 'now', new DateTimeZone( 'UTC' ) ); + + if ( count( $heading ) ) { + $version = $heading[1]; + $timestamp = $heading[2]; + + try { + $timestamp = new DateTime( $timestamp, new DateTimeZone( 'UTC' ) ); + } catch ( \Exception $ex ) { + throw new InvalidArgumentException( "Heading has an invalid timestamp: $heading", 0, $ex ); + } + + if ( strtotime( $heading[2], 0 ) !== strtotime( $heading[2], 1000000000 ) ) { + throw new InvalidArgumentException( "Heading has a relative timestamp: $heading" ); + } + $entry_timestamp = $timestamp; + + $content = trim( preg_replace( $heading_pattern, '', $section ) ); + } elseif ( $is_subentry ) { + // It must be a subheading. + $version = $subheading[0]; // For now. + $content = trim( preg_replace( $subentry_pattern, '', $section ) ); + } + + $entry = $this->newChangelogEntry( + $version, + array( + 'timestamp' => $timestamp, + ) + ); + + $entries[] = $entry; + + if ( '' === $content ) { + // Huh, no changes. + continue; + } + + // Now parse all the subheadings and changes. + while ( '' !== $content ) { + $changes = array(); + $rows = explode( "\n", $content ); + foreach ( $rows as $row ) { + $row = trim( $row ); + $row = preg_replace( '/' . $this->bullet . '/', '', $row, 1 ); + $row_segments = explode( ' - ', $row ); + + array_push( + $changes, + array( + 'subheading' => $is_subentry ? '' : trim( $row_segments[0] ), + 'content' => $is_subentry ? trim( $row ) : trim( $row_segments[1] ), + ) + ); + } + + foreach ( $changes as $change ) { + $entry->appendChange( + $this->newChangeEntry( + array( + 'subheading' => $change['subheading'], + 'content' => $change['content'], + 'timestamp' => $entry_timestamp, + ) + ) + ); + } + $content = ''; + } + } + + $ret->setEntries( $entries ); + $ret->setPrologue( $this->prologue ); + $ret->setEpilogue( $this->epilogue ); + return $ret; + } + + /** + * Write a Changelog object to a string. + * + * @param Changelog $changelog Changelog object. + * @return string + */ + public function format( Changelog $changelog ) { + $ret = ''; + $date_format = 'Y-m-d'; + $bullet = $this->bullet; + $indent = str_repeat( ' ', strlen( $bullet ) ); + + $prologue = trim( $changelog->getPrologue() ); + if ( '' !== $prologue ) { + $ret .= "$prologue\n\n"; + } + + foreach ( $changelog->getEntries() as $entry ) { + $version = $entry->getVersion(); + $is_subentry = preg_match( $this->subentry_pattern, $version, $subentry ); + $timestamp = $entry->getTimestamp(); + $release_link = $this->getReleaseLink( $version ); + + if ( $is_subentry ) { + $ret .= '###' . $subentry[1] . " \n\n"; + } else { + $ret .= '## [' . $version . '](' . $release_link . ') - ' . $timestamp->format( $date_format ) . " \n\n"; + } + + $prologue = trim( $entry->getPrologue() ); + + if ( '' !== $prologue ) { + $ret .= "$prologue\n\n"; + } + + foreach ( $entry->getChangesBySubheading() as $heading => $changes ) { + foreach ( $changes as $change ) { + $breaking_change = 'major' === $change->getSignificance() ? ' [ **BREAKING CHANGE** ]' : ''; + $text = trim( $change->getContent() ); + if ( '' !== $text ) { + $preamble = $is_subentry ? '' : $bullet . $heading . $breaking_change . ' - '; + $ret .= $preamble . str_replace( "\n", "\n$indent", $text ) . "\n"; + } + } + } + $ret = trim( $ret ) . "\n\n"; + } + + $epilogue = trim( $changelog->getEpilogue() ); + if ( '' !== $epilogue ) { + $ret .= "$epilogue\n"; + } + + $ret = trim( $ret ) . "\n"; + + return $ret; + } +} diff --git a/tools/changelogger/PackageFormatter.php b/tools/changelogger/PackageFormatter.php new file mode 100644 index 00000000000..0d3000fe0ae --- /dev/null +++ b/tools/changelogger/PackageFormatter.php @@ -0,0 +1,27 @@ +