Add Jetpack Changelogger

This commit is contained in:
Paul Sealock 2021-11-11 16:28:10 +13:00
parent ba87a69d6f
commit a5abab9ab0
15 changed files with 620 additions and 119 deletions

View File

@ -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"
]
}
}

View File

View File

@ -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"
]
}
}

View File

@ -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"
]
}
}

View File

@ -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"
]
}
}

View File

View File

@ -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"
]
}
}

View File

View File

View File

@ -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"
}
}
}

View File

@ -0,0 +1,273 @@
<?php
/**
* Base Jetpack Changelogger Formatter for WooCommerce
*/
use Automattic\Jetpack\Changelog\Changelog;
use Automattic\Jetpack\Changelog\KeepAChangelogParser;
use Automattic\Jetpack\Changelogger\FormatterPlugin;
use Automattic\Jetpack\Changelogger\PluginTrait;
/**
* Base Jetpack Changelogger Formatter for WooCommerce
*
* Class Formatter
*/
class Formatter extends KeepAChangelogParser implements FormatterPlugin {
use PluginTrait;
/**
* Bullet for changes.
*
* @var string
*/
public $bullet = '- ';
/**
* Prologue text.
*
* @var string
*/
public $prologue = "# Changelog \n\n";
/**
* Epilogue text.
*
* @var string
*/
public $epilogue = '';
/**
* Entry pattern regex.
*
* @var string
*/
public $entry_pattern = '/^##\s+([^\n=]+)\s+((?:(?!^##).)+)/ms';
/**
* Heading pattern regex.
*
* @var string
*/
public $heading_pattern = '/^## \[+(\[?[^] ]+\]?)\]\(.+\) - (.+?)\n/s';
/**
* Subheading pattern regex.
*
* @var string
*/
public $subentry_pattern = '/^###(.+)\n/m';
/**
* Get Release link given a version number.
*
* @throws InvalidArgumentException When directory parsing fails.
* @param string $version Release version.
*
* @return string Link to the version's release.
*/
public function getReleaseLink( $version ) {
$path_map = array(
'packages/js/components' => '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;
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Jetpack Changelogger Formatter for WooCommerce packages
*/
require_once 'Formatter.php';
/**
* Jetpack Changelogger Formatter for WooCommerce Packages
*
* Class Formatter
*/
class PackageFormatter extends Formatter {
/**
* Prologue text.
*
* @var string
*/
public $prologue = "# Changelog \n\nThis project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).";
/**
* Epilogue text.
*
* @var string
*/
public $epilogue = "---\n\n[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce-admin/blob/main/packages/components/CHANGELOG.md).";
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Jetpack Changelogger Formatter for WooCommerce plugins
*/
require_once 'Formatter.php';
/**
* Jetpack Changelogger Formatter for WooCommerce Plugins
*
* Class Formatter
*/
class PluginFormatter extends Formatter {
/**
* Epilogue text.
*
* @var string
*/
public $epilogue = "---\n\n[See changelogs for previous versions](https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/changelog.txt).";
/**
* Entry pattern regex.
*
* @var string
*/
public $entry_pattern = '/^##?#\s+([^\n=]+)\s+((?:(?!^##).)+)/ms';
}