From bf1e9923b349a0387706f1f166fb371d13e7a75a Mon Sep 17 00:00:00 2001 From: Lars Strojny Date: Fri, 3 Apr 2020 19:16:28 +0200 Subject: [PATCH] Add release tooling --- .gitignore | 4 +- composer.json | 37 +++--- release.php | 45 ------- tools/functions.php | 273 +++++++++++++++++++++++++++++++++++++++++ tools/make-release.php | 22 ++++ 5 files changed, 320 insertions(+), 61 deletions(-) delete mode 100644 release.php create mode 100644 tools/functions.php create mode 100644 tools/make-release.php diff --git a/.gitignore b/.gitignore index 66f887e8..108619ac 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,6 @@ cmake-build-*/ CMakeLists.txt # CLion -.idea/ \ No newline at end of file +.idea/ +/vendor/ +/composer.lock diff --git a/composer.json b/composer.json index da89bd02..545ec1d1 100644 --- a/composer.json +++ b/composer.json @@ -1,18 +1,25 @@ { - "name": "pdezwart/php-amqp", - "type": "library", - "description": "PHP AMQP Binding Library", - "keywords": ["rabbitmq", "amqp", "message", "queue"], - "homepage": "https://github.com/pdezwart/php-amqp", - "license": "PHP-3.01", - "authors": [ - { - "name": "Pieter de Zwart", - "email": "pdezwart@php.net", - "role": "lead" - } - ], - "require": { - "php": ">=5.3.0" + "name": "pdezwart/php-amqp", + "type": "library", + "description": "PHP AMQP Binding Library", + "keywords": [ + "rabbitmq", + "amqp", + "message", + "queue" + ], + "homepage": "https://github.com/pdezwart/php-amqp", + "license": "PHP-3.01", + "authors": [ + { + "name": "Pieter de Zwart", + "email": "pdezwart@php.net", + "role": "lead" } + ], + "require": { + "php": ">=7.1", + "ext-dom": "*", + "ext-simplexml": "*" + } } diff --git a/release.php b/release.php deleted file mode 100644 index 9fed765c..00000000 --- a/release.php +++ /dev/null @@ -1,45 +0,0 @@ -addChild('file'); - $child->addAttribute('name', $file); - $child->addAttribute('role', $role); - } -} - -function removeNodes(SimpleXMLElement $el, $expression) { - $nodesToDelete = []; - - foreach ($el->children() as $file) { - if (fnmatch($expression, (string) $file['name'])) { - $nodesToDelete[] = $file; - } - } - - foreach ($nodesToDelete as $node) { - $dom = dom_import_simplexml($node); - $dom->parentNode->removeChild($dom); - } -} - -removeNodes($xml->contents->dir, $sourceExpression); -removeNodes($xml->contents->dir, $testsExpression); -removeNodes($xml->contents->dir, $stubExpression); -addFiles($xml->contents->dir, $sourceExpression, 'src'); -addFiles($xml->contents->dir, 'tests/' . $testsExpression, 'test'); -addFiles($xml->contents->dir, $stubExpression, 'doc'); - -$xml->saveXML('package.xml'); - -`xmlformat --in-place package.xml`; -`xmlstarlet format --indent-spaces 4 package.xml > package.xml.new`; -rename('package.xml.new', 'package.xml'); diff --git a/tools/functions.php b/tools/functions.php new file mode 100644 index 00000000..8b3fb5d8 --- /dev/null +++ b/tools/functions.php @@ -0,0 +1,273 @@ +' . VERSION_REGEX . ')"'; + +function re(string ...$regex): string +{ + return '@^' . str_replace('@', '\\@', implode('', $regex)) . '$@mD'; +} + +function gitFetch(): void +{ + `git fetch --all`; +} + +function packageXml(): DOMDocument +{ + static $doc; + + if (!$doc) { + $doc = new DOMDocument('1.0', 'UTF-8'); + $doc->formatOutput = true; + $doc->preserveWhiteSpace = false; + $doc->load(PACKAGE_XML); + } + + return $doc; +} + +function savePackageXml(): void +{ + packageXml()->save(PACKAGE_XML); + $packageXml = PACKAGE_XML; + $pretty = `XMLLINT_INDENT=" " xmllint --format $packageXml`; + file_put_contents(PACKAGE_XML, $pretty); +} + +function getPackageVersion(): string +{ + $xml = simplexml_import_dom(packageXml()); + + $release = $xml->version->release; + + assert(preg_match(re(VERSION_REGEX), $release)); + + return $release; +} + +function setPackageVersion(string $nextVersion): void +{ + $xml = simplexml_import_dom(packageXml()); + + $xml->version->release = $nextVersion; +} + +function setChangelog(string $changelog): void +{ + $xml = simplexml_import_dom(packageXml()); + + $noteNode = dom_import_simplexml($xml->notes); + foreach ($noteNode->childNodes as $child) { + $noteNode->removeChild($child); + } + $noteNode->appendChild(packageXml()->createCDATASection($changelog)); +} + +function setDate(DateTimeImmutable $now): void +{ + $xml = simplexml_import_dom(packageXml()); + + $xml->date = $now->format('Y-m-d'); + $xml->time = $now->format('H:i:s'); +} + +function addFilesToPackageXml(SimpleXMLElement $dir, string $expression, string $role): void { + foreach (glob(BASE_DIR . $expression) as $file) { + + $file = str_replace(realpath(BASE_DIR) . '/', '', realpath($file)); + + if ($file === 'config.h') { + continue; + } + + $child = $dir->addChild('file'); + $child->addAttribute('name', $file); + $child->addAttribute('role', $role); + } +} + +function removeFromPackageXmlNodes(SimpleXMLElement $el, string $expression): void { + $nodesToDelete = []; + + foreach ($el->children() as $file) { + if (fnmatch($expression, (string) $file['name'])) { + $nodesToDelete[] = $file; + } + } + + foreach ($nodesToDelete as $node) { + $dom = dom_import_simplexml($node); + $dom->parentNode->removeChild($dom); + } +} + +function updateFiles(): void { + $xml = simplexml_import_dom(packageXml()); + $sourceExpression = '*.[ch]'; + $testsExpression = '*.phpt'; + $stubExpression = 'stubs/*.php'; + + removeFromPackageXmlNodes($xml->contents->dir, $sourceExpression); + removeFromPackageXmlNodes($xml->contents->dir, $testsExpression); + removeFromPackageXmlNodes($xml->contents->dir, $stubExpression); + addFilesToPackageXml($xml->contents->dir, $sourceExpression, 'src'); + addFilesToPackageXml($xml->contents->dir, 'tests/' . $testsExpression, 'test'); + addFilesToPackageXml($xml->contents->dir, $stubExpression, 'doc'); +} + +function getSourceVersion(): string +{ + $content = file_get_contents(HEADER_VERSION_FILE); + + assert(preg_match(re(SOURCE_VERSION_REGEX), $content, $matches)); + assert(preg_match(re(VERSION_REGEX), $matches['version'])); + + return $matches['version']; +} + +function setSourceVersion(string $nextVersion): void +{ + file_put_contents( + HEADER_VERSION_FILE, + preg_replace( + re(SOURCE_VERSION_REGEX), + '\1"' . $nextVersion . '"', + file_get_contents(HEADER_VERSION_FILE) + ) + ); +} + +function getPreviousVersion(): string +{ + $previousVersion = ltrim(trim(`git tag -l --sort=-v:refname | head -n1`), 'v'); + + assert(preg_match(re(VERSION_REGEX), $previousVersion)); + + return $previousVersion; +} + +function versionToTag(string $version): string +{ + return sprintf('v%s', $version); +} + +function isIgnoredMessage(string $message): bool +{ + foreach (COMMIT_MESSAGE_CHANGELOG_IGNORED as $pattern) { + return strpos($message, $pattern) !== false; + } + + return false; +} + +function buildChangelog(string $nextVersion, string $previousVersion): string +{ + $previousTag = versionToTag($previousVersion); + $nextTag = versionToTag($nextVersion); + + $commits = explode("\n", trim(`git log --oneline ${previousTag}..origin/master --pretty=%h --no-merges`)); + + $changeLines = []; + + foreach ($commits as $commit) { + $committerName = trim(`git log $commit^..$commit --pretty=%an`); + $committerEmail = trim(`git log $commit^..$commit --pretty=%ae`); + $message = trim(`git log $commit^..$commit --pretty=%s`); + + if (isIgnoredMessage($message)) { + continue; + } + + $issueId = null; + if (preg_match('/^(?P.*)\s+\(#(?P\d+)\)$/', $message, $matches)) { + $message = $matches['message']; + $issueId = $matches['issueId']; + } + + $url = $issueId ? sprintf(ISSUE_URL_TEMPLATE, $issueId): sprintf(COMMIT_URL_TEMPLATE, $commit); + $committer = strpos($committerEmail, '@users.noreply.github.com') !== false + ? $committerName + : sprintf('%s <%s>', $committerName, $committerEmail); + $changeLines[] = sprintf(' - %s (%s) (%s)', ucfirst($message), $committer, $url); + } + + $changes = implode(PHP_EOL, $changeLines); + $changelog = <<