From fc5bd86a3bfa229e6dc8d1a89a2a6c3acb1adbc7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 17:16:38 +0000 Subject: [PATCH 1/7] fix(composer): Correct package metadata and autoloading Add the description, license, homepage, keywords, support links and PHP version constraint that were missing from composer.json, and point the authors entry at Seam Labs rather than a personal address. Move the Tests\ PSR-4 mapping from autoload to autoload-dev so the test namespace is no longer registered in consumer projects, and add an archive exclude list so a built package ships only the runtime files. package.json is deliberately kept in the archive because Seam\Utils\PackageVersion reads it at runtime. Define the primary development tasks as Composer scripts (build, test, lint) and configure PHPUnit in phpunit.xml.dist so the suite is declared rather than passed by path. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RFK47311nWtbd3gHmjdknR --- .editorconfig | 3 - .gitignore | 4 + LICENSE.txt | 20 +++ composer.json | 92 +++++++++-- composer.lock | 392 +++++++++++++++++++++++++++++++++++------------ phpunit.xml.dist | 22 +++ 6 files changed, 424 insertions(+), 109 deletions(-) create mode 100644 LICENSE.txt create mode 100644 phpunit.xml.dist diff --git a/.editorconfig b/.editorconfig index 61ea5dd7..f100c09b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,3 @@ indent_size = 2 [*.php] indent_size = 4 - -[Makefile] -indent_style = tab diff --git a/.gitignore b/.gitignore index 56327be0..78aaf436 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,12 @@ CODEGEN.md vendor tmp +# PHPUnit result cache +.phpunit.result.cache + # Build directories package +/pkg/ # Environment versions file .versions diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..ebaae486 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2021-2024 Seam Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/composer.json b/composer.json index b8d0b9b4..7e4c0eab 100644 --- a/composer.json +++ b/composer.json @@ -1,24 +1,94 @@ { "name": "seamapi/seam", + "description": "SDK for the Seam API written in PHP.", "type": "library", + "keywords": [ + "seam", + "sdk", + "api", + "iot", + "smart-lock", + "access-control" + ], + "homepage": "https://github.com/seamapi/php", + "license": "MIT", + "authors": [ + { + "name": "Seam Labs, Inc.", + "email": "engineering@getseam.com", + "homepage": "https://www.seam.co" + } + ], + "support": { + "docs": "https://docs.seam.co", + "issues": "https://github.com/seamapi/php/issues", + "source": "https://github.com/seamapi/php" + }, "require": { + "php": "^8.0", "guzzlehttp/guzzle": "^7.5" }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.7" + }, "autoload": { "psr-4": { - "Seam\\": ["src/", "src/Exceptions/"], - "Tests\\": "tests/" + "Seam\\": [ + "src/", + "src/Exceptions/" + ] }, - "classmap": ["src/Resources/"] + "classmap": [ + "src/Resources/" + ] }, - "authors": [ - { - "name": "seveibar", - "email": "seveibar@gmail.com" + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" } - ], - "require-dev": { - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.7" + }, + "config": { + "sort-packages": true + }, + "archive": { + "exclude": [ + "/.devcontainer", + "/.github", + "/codegen", + "/node_modules", + "/pkg", + "/tests", + "/tmp", + "/vendor", + "/.editorconfig", + "/.env.example", + "/.npmrc", + "/.phpunit.result.cache", + "/.prettierignore", + "/.prettierrc.json", + "/.releaserc.json", + "/eslint.config.ts", + "/package-lock.json", + "/phpunit.xml.dist", + "/tsconfig.json" + ] + }, + "scripts": { + "build": "@composer archive --format=zip --dir=pkg --file=seam", + "test": "phpunit", + "lint": [ + "@lint:composer", + "@lint:syntax" + ], + "lint:composer": "@composer validate --strict", + "lint:syntax": "! find src tests -name '*.php' -exec php -l {} \\; | grep -v '^No syntax errors detected'" + }, + "scripts-descriptions": { + "build": "Build a distributable archive of this package into pkg/.", + "test": "Run the test suite.", + "lint": "Run all lint checks.", + "lint:composer": "Validate composer.json and composer.lock.", + "lint:syntax": "Check every PHP source file for syntax errors." } } diff --git a/composer.lock b/composer.lock index 55187fba..bc6794ae 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dbef2f8bca9faca558cad1eb1b7a1771", + "content-hash": "47b28c106be0ff4f75680c1fe23e931a", "packages": [ { "name": "guzzlehttp/guzzle", @@ -54,13 +54,17 @@ } }, "autoload": { - "files": ["src/functions_include.php"], + "files": [ + "src/functions_include.php" + ], "psr-4": { "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -157,13 +161,17 @@ } }, "autoload": { - "files": ["src/functions_include.php"], + "files": [ + "src/functions_include.php" + ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -187,7 +195,9 @@ } ], "description": "Guzzle promises library", - "keywords": ["promise"], + "keywords": [ + "promise" + ], "support": { "issues": "https://github.com/guzzle/promises/issues", "source": "https://github.com/guzzle/promises/tree/1.5.2" @@ -256,7 +266,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -355,7 +367,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -364,7 +378,12 @@ ], "description": "Common interface for HTTP clients", "homepage": "https://github.com/php-fig/http-client", - "keywords": ["http", "http-client", "psr", "psr-18"], + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], "support": { "source": "https://github.com/php-fig/http-client/tree/master" }, @@ -400,7 +419,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -452,7 +473,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -497,10 +520,14 @@ }, "type": "library", "autoload": { - "files": ["src/getallheaders.php"] + "files": [ + "src/getallheaders.php" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Ralph Khattar", @@ -542,10 +569,14 @@ } }, "autoload": { - "files": ["function.php"] + "files": [ + "function.php" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -613,7 +644,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Marco Pivetta", @@ -623,7 +656,10 @@ ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": ["constructor", "instantiate"], + "keywords": [ + "constructor", + "instantiate" + ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", "source": "https://github.com/doctrine/instantiator/tree/1.4.1" @@ -672,15 +708,25 @@ }, "type": "library", "autoload": { - "files": ["src/DeepCopy/deep_copy.php"], + "files": [ + "src/DeepCopy/deep_copy.php" + ], "psr-4": { "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "Create deep copies (clones) of your objects", - "keywords": ["clone", "copy", "duplicate", "object", "object graph"], + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" @@ -715,7 +761,9 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, - "bin": ["bin/php-parse"], + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { @@ -728,14 +776,19 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Nikita Popov" } ], "description": "A PHP parser written in PHP", - "keywords": ["parser", "php"], + "keywords": [ + "parser", + "php" + ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" @@ -770,10 +823,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Arne Blankerts", @@ -817,10 +874,14 @@ }, "type": "library", "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Arne Blankerts", @@ -888,10 +949,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -901,7 +966,11 @@ ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": ["coverage", "testing", "xunit"], + "keywords": [ + "coverage", + "testing", + "xunit" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17" @@ -941,10 +1010,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -954,7 +1027,10 @@ ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": ["filesystem", "iterator"], + "keywords": [ + "filesystem", + "iterator" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" @@ -998,10 +1074,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1011,7 +1091,9 @@ ], "description": "Invoke callables with a timeout", "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": ["process"], + "keywords": [ + "process" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" @@ -1051,10 +1133,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1064,7 +1150,9 @@ ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": ["template"], + "keywords": [ + "template" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" @@ -1104,10 +1192,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1117,7 +1209,9 @@ ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": ["timer"], + "keywords": [ + "timer" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" @@ -1177,7 +1271,9 @@ "ext-soap": "*", "ext-xdebug": "*" }, - "bin": ["phpunit"], + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { @@ -1185,11 +1281,17 @@ } }, "autoload": { - "files": ["src/Framework/Assert/Functions.php"], - "classmap": ["src/"] + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1199,7 +1301,11 @@ ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", - "keywords": ["phpunit", "testing", "xunit"], + "keywords": [ + "phpunit", + "testing", + "xunit" + ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24" @@ -1243,10 +1349,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1295,10 +1405,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1347,10 +1461,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1400,10 +1518,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1424,7 +1546,11 @@ ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": ["comparator", "compare", "equality"], + "keywords": [ + "comparator", + "compare", + "equality" + ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" @@ -1465,10 +1591,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1518,10 +1648,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1534,7 +1668,12 @@ ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": ["diff", "udiff", "unidiff", "unified diff"], + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" @@ -1577,10 +1716,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1589,7 +1732,11 @@ ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": ["Xdebug", "environment", "hhvm"], + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" @@ -1631,10 +1778,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1659,7 +1810,10 @@ ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": ["export", "exporter"], + "keywords": [ + "export", + "exporter" + ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" @@ -1705,10 +1859,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1717,7 +1875,9 @@ ], "description": "Snapshotting of global state", "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": ["global state"], + "keywords": [ + "global state" + ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" @@ -1758,10 +1918,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1812,10 +1976,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1863,10 +2031,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1914,10 +2086,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -1973,10 +2149,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -2024,10 +2204,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -2073,10 +2257,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -2103,12 +2291,12 @@ "version": "3.7.1", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", "shasum": "" }, @@ -2121,7 +2309,10 @@ "require-dev": { "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, - "bin": ["bin/phpcs", "bin/phpcbf"], + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], "type": "library", "extra": { "branch-alias": { @@ -2129,7 +2320,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Greg Sherwood", @@ -2138,7 +2331,10 @@ ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": ["phpcs", "standards"], + "keywords": [ + "phpcs", + "standards" + ], "support": { "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", "source": "https://github.com/squizlabs/PHP_CodeSniffer", @@ -2168,10 +2364,14 @@ }, "type": "library", "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Arne Blankerts", @@ -2195,10 +2395,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform": { + "php": "^8.0" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..e953736f --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + tests + + + + + src + + + From 393fdfea910409c252f715c3a6ef16063bd5342c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 18:48:29 +0000 Subject: [PATCH 2/7] chore: Remove Renovate and set CODEOWNERS to the sdk team renovate.json used the deprecated config:base preset and only enabled updates for @seamapi/types, which Dependabot already covers, so it was doing nothing that the npm ecosystem in dependabot.yml does not. Composer dependencies stay under manual control. Set CODEOWNERS to the sdk team to match the other SDK repositories. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RFK47311nWtbd3gHmjdknR --- .github/CODEOWNERS | 2 +- renovate.json | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 renovate.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b1dab4d9..c3d9f4b0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @rchodava @andrii-balitskyi @seveibar +* @seamapi/sdk diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 431586d4..00000000 --- a/renovate.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:base"], - "commitMessagePrefix": "autorelease:", - "packageRules": [ - { - "packagePatterns": ["*"], - "excludePackagePatterns": ["@seamapi/types"], - "enabled": false - }, - { - "matchUpdateTypes": ["major", "minor", "patch"], - "automerge": true - } - ] -} From 29b9a441fc5672b7911915770f0c4be2c3f04dbc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 17:16:54 +0000 Subject: [PATCH 3/7] ci: Bring GitHub Actions up to parity with the other SDKs Replace the bespoke test workflow with a Check workflow modelled on the Ruby and Python SDKs. It runs on pushes to main as well as pull requests, has test, lint, build and install jobs, sets job timeouts and drops the SEAM_API_KEY secret the suite no longer needs, so checks now run on forks. Add a real PHP setup composite action that installs PHP, validates the lockfile and installs Composer dependencies from a cache. The existing action only set up Node.js, so rename it to setup-node and repoint the generate and version workflows at it. Add a reusable build workflow that packages the library with composer build and uploads the archive, an install job that requires that archive from a path repository and constructs a SeamClient, and wire the archive into the Publish workflow, which previously created a release with no files. Add a Format workflow so pushes are formatted with Prettier and committed, and widen Semantic Release to all branches so the prerelease branches declared in .releaserc.json can actually release. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RFK47311nWtbd3gHmjdknR --- .github/actions/setup-node/action.yml | 46 +++++++++ .github/actions/setup/action.yml | 55 ++++++----- .github/workflows/_build.yml | 36 +++++++ .github/workflows/check.yml | 124 +++++++++++++++++++++++++ .github/workflows/format.yml | 41 ++++++++ .github/workflows/generate.yml | 4 +- .github/workflows/publish.yml | 15 ++- .github/workflows/semantic-release.yml | 2 +- .github/workflows/test.yml | 18 ---- .github/workflows/version.yml | 4 +- 10 files changed, 291 insertions(+), 54 deletions(-) create mode 100644 .github/actions/setup-node/action.yml create mode 100644 .github/workflows/_build.yml create mode 100644 .github/workflows/check.yml create mode 100644 .github/workflows/format.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml new file mode 100644 index 00000000..d52103d3 --- /dev/null +++ b/.github/actions/setup-node/action.yml @@ -0,0 +1,46 @@ +--- +name: Setup Node.js +description: Setup Node.js and install dependencies. + +inputs: + node_version: + description: The Node.js version. + required: false + default: '24' + registry_url: + description: The Node.js package registry URL. + required: false + default: https://registry.npmjs.org + install_dependencies: + description: Install dependencies. + required: false + default: 'true' + +runs: + using: composite + steps: + - name: Setup Node.js + uses: actions/setup-node@v7 + if: inputs.install_dependencies == 'true' + with: + node-version: ${{ inputs.node_version }} + registry-url: ${{ inputs.registry_url }} + - name: Setup Node.js without cache + uses: actions/setup-node@v7 + if: inputs.install_dependencies == 'false' + with: + node-version: ${{ inputs.node_version }} + registry-url: ${{ inputs.registry_url }} + - name: Install dependencies + if: inputs.install_dependencies == 'true' + shell: bash + run: npm ci --ignore-scripts + - name: Rebuild Node.js modules + shell: bash + run: npm rebuild + - name: Run postinstall script + shell: bash + run: npm run postinstall --if-present + - name: Run prepare script + shell: bash + run: npm run prepare --if-present diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index e0b54475..0943da63 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,15 +1,12 @@ --- name: Setup -description: Setup Node.js and install dependencies. +description: Setup PHP and install dependencies. + inputs: - node_version: - description: The Node.js version. - required: false - default: '24' - registry_url: - description: The Node.js package registry URL. + php_version: + description: The PHP version. required: false - default: https://registry.npmjs.org + default: '8.4' install_dependencies: description: Install dependencies. required: false @@ -18,28 +15,30 @@ inputs: runs: using: composite steps: - - name: Setup Node.js - uses: actions/setup-node@v7 - if: inputs.install_dependencies == 'true' + - name: Setup PHP + uses: shivammathur/setup-php@v2 with: - node-version: ${{ inputs.node_version }} - registry-url: ${{ inputs.registry_url }} - - name: Setup Node.js without cache - uses: actions/setup-node@v7 - if: inputs.install_dependencies == 'false' - with: - node-version: ${{ inputs.node_version }} - registry-url: ${{ inputs.registry_url }} - - name: Install dependencies + php-version: ${{ inputs.php_version }} + tools: composer:v2 + coverage: none + - name: Check lockfile if: inputs.install_dependencies == 'true' shell: bash - run: npm ci --ignore-scripts - - name: Rebuild Node.js modules - shell: bash - run: npm rebuild - - name: Run postinstall script + run: composer validate --strict + - name: Get Composer cache directory + id: composer_cache + if: inputs.install_dependencies == 'true' shell: bash - run: npm run postinstall --if-present - - name: Run prepare script + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Setup Composer cache + uses: actions/cache@v4 + if: inputs.install_dependencies == 'true' + with: + key: composer-${{ inputs.php_version }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('composer.lock') }} + restore-keys: | + composer-${{ inputs.php_version }}-${{ runner.os }}-${{ runner.arch }}- + path: ${{ steps.composer_cache.outputs.dir }} + - name: Install dependencies + if: inputs.install_dependencies == 'true' shell: bash - run: npm run prepare --if-present + run: composer install --no-interaction --no-progress diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml new file mode 100644 index 00000000..31d46310 --- /dev/null +++ b/.github/workflows/_build.yml @@ -0,0 +1,36 @@ +--- +name: _build + +on: + workflow_call: + inputs: + php_version: + description: The PHP version. + type: string + required: false + default: '8.4' + outputs: + artifact_name: + description: The artifact name. + value: build-${{ github.sha }} + +jobs: + build: + name: Package + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Setup + uses: ./.github/actions/setup + with: + php_version: ${{ inputs.php_version }} + - name: Build + run: composer build + - name: Upload artifact + uses: actions/upload-artifact@v7 + with: + name: build-${{ github.sha }} + if-no-files-found: error + path: pkg/ diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000..60d0be57 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,124 @@ +--- +name: Check + +on: + push: + branches: + - main + pull_request: + branches: + - '**' + +jobs: + test: + name: Test (PHP ${{ matrix.php }} on ${{ matrix.os_name }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + php: + - '8.4' + - '8.5' + include: + - os: ubuntu-latest + os_name: Linux + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Setup + uses: ./.github/actions/setup + with: + php_version: ${{ matrix.php }} + - name: Setup Node.js + uses: ./.github/actions/setup-node + - name: Test + run: composer test + lint: + name: Lint (PHP ${{ matrix.php }}) + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + php: + - '8.4' + - '8.5' + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Setup + uses: ./.github/actions/setup + with: + php_version: ${{ matrix.php }} + - name: Setup Node.js + uses: ./.github/actions/setup-node + - name: Lint + run: composer lint + - name: Lint with ESLint and Prettier + run: npm run lint + build: + name: Build + uses: ./.github/workflows/_build.yml + install: + name: Install (PHP ${{ matrix.php }} on ${{ matrix.os_name }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + needs: build + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + php: + - '8.4' + - '8.5' + include: + - os: ubuntu-latest + os_name: Linux + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + coverage: none + - name: Download artifact + uses: actions/download-artifact@v8 + with: + name: ${{ needs.build.outputs.artifact_name }} + path: pkg/ + - name: Extract package + run: unzip -q pkg/seam.zip -d package + - name: Create composer.json + uses: DamianReeves/write-file-action@v1.3 + with: + write-mode: overwrite + path: composer.json + contents: | + { + "repositories": [ + { + "type": "path", + "url": "./package", + "options": { "symlink": false } + } + ], + "require": { "seamapi/seam": "*" }, + "minimum-stability": "dev" + } + - name: Create main.php + uses: DamianReeves/write-file-action@v1.3 + with: + write-mode: overwrite + path: main.php + contents: | + diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index dd5e1472..f163e526 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -27,8 +27,8 @@ jobs: git_committer_email: ${{ secrets.GIT_USER_EMAIL }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - - name: Setup - uses: ./.github/actions/setup + - name: Setup Node.js + uses: ./.github/actions/setup-node with: install_dependencies: 'false' - name: Normalize package-lock.json diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 42ac2323..506ee053 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,3 +1,4 @@ +--- name: Publish run-name: Publish ${{ github.ref_name }} @@ -8,16 +9,24 @@ on: - v* jobs: + build: + name: Build + uses: ./.github/workflows/_build.yml release: name: GitHub Releases runs-on: ubuntu-latest timeout-minutes: 30 + needs: build steps: - name: Checkout uses: actions/checkout@v7 with: fetch-depth: 0 - + - name: Download artifact + uses: actions/download-artifact@v8 + with: + name: ${{ needs.build.outputs.artifact_name }} + path: pkg/ - name: Generate release notes id: changelog run: | @@ -26,11 +35,11 @@ jobs: echo "outfile=${outfile}" >> $GITHUB_OUTPUT npx standard-changelog@^5.0.0 --release-count 2 --infile $outfile.tmp --outfile $outfile.tmp sed '1,3d' $outfile.tmp > $outfile - - name: Create GitHub release uses: softprops/action-gh-release@v3 with: token: ${{ secrets.GH_TOKEN }} + fail_on_unmatched_files: true prerelease: ${{ contains(github.ref_name, '-') }} - files: '' + files: pkg/* body_path: ${{ github.workspace }}/${{ steps.changelog.outputs.outfile }} diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml index 88a1dfc1..b2e3aab9 100644 --- a/.github/workflows/semantic-release.yml +++ b/.github/workflows/semantic-release.yml @@ -6,7 +6,7 @@ run-name: Semantic Release from ${{ github.ref_name }} on: push: branches: - - main + - '**' concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 7f897d7d..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: PHP Test -on: ['pull_request'] -jobs: - test: - if: "!contains(github.event.head_commit.message, 'skip ci')" - name: Run PHP Tests - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v7 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.0' - - run: composer install - - run: composer exec phpunit tests - env: - SEAM_API_KEY: ${{ secrets.SEAM_API_KEY }} diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 7435c6d2..3d9a72f8 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -29,7 +29,7 @@ jobs: git_committer_email: ${{ secrets.GIT_USER_EMAIL }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - - name: Setup - uses: ./.github/actions/setup + - name: Setup Node.js + uses: ./.github/actions/setup-node - name: Cut ${{ github.event.inputs.version }} version run: npm version --sign-git-tag=true ${{ github.event.inputs.version }} From ce582328fd2fcc193f051d8bf54f744f2b91bb03 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 17:17:04 +0000 Subject: [PATCH 4/7] fix(prettier): Apply the PHP parser only to PHP files The top-level parser option forced the PHP parser onto every file in the repository. Non-PHP files were parsed as inline HTML and written back unchanged, so Prettier silently did nothing for TypeScript, JSON, YAML and Markdown, and the overrides block only ever adjusted options that the PHP parser ignored. Move the PHP parser into an override on **/*.php with the plugin defaults it was already producing, and let Prettier infer the parser for everything else, which restores the shared options used by the Ruby and Python SDKs. Disable embedded formatting in Markdown so the PHP examples in the README keep the style of the generated source. The codegen changes are the result of running the formatter over TypeScript for the first time. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RFK47311nWtbd3gHmjdknR --- .prettierrc.json | 21 ++++++++++++++------- codegen/lib/handlebars-helpers.ts | 8 ++++++-- codegen/lib/resource-model.ts | 18 ++++++++++++++---- codegen/lib/routes.ts | 25 +++++++++++++------------ 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 4e3a60e5..66ce34de 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,15 +1,22 @@ { "plugins": ["@prettier/plugin-php"], - "parser": "php", - + "semi": false, + "singleQuote": true, + "jsxSingleQuote": true, + "endOfLine": "lf", "overrides": [ { - "files": "**/*.{js,ts,json,yml,md}", + "files": "**/*.php", "options": { - "semi": false, - "singleQuote": true, - "jsxSingleQuote": true, - "endOfLine": "lf" + "parser": "php", + "semi": true, + "singleQuote": false + } + }, + { + "files": "**/*.md", + "options": { + "embeddedLanguageFormatting": "off" } } ] diff --git a/codegen/lib/handlebars-helpers.ts b/codegen/lib/handlebars-helpers.ts index 41238d38..263be9ce 100644 --- a/codegen/lib/handlebars-helpers.ts +++ b/codegen/lib/handlebars-helpers.ts @@ -52,10 +52,14 @@ const createPhpDoc = ( .split(/\r?\n/) .map(sanitizePhpDocLine) const populatedDescription = description.trim() === '' ? [] : descriptionLines - const populatedTags = tags.filter((tag) => tag.trim() !== '').map(sanitizePhpDocLine) + const populatedTags = tags + .filter((tag) => tag.trim() !== '') + .map(sanitizePhpDocLine) const lines = [ ...populatedDescription, - ...(populatedDescription.length > 0 && populatedTags.length > 0 ? [''] : []), + ...(populatedDescription.length > 0 && populatedTags.length > 0 + ? [''] + : []), ...populatedTags, ] diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index 0289112e..e29a192f 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -16,8 +16,14 @@ import { getPhpType } from './map-php-type.js' export type ResourceClassProperty = | ({ kind: 'value'; phpType: string } & ResourceClassPropertyMetadata) - | ({ kind: 'objectReference'; referenceName: string } & ResourceClassPropertyMetadata) - | ({ kind: 'listReference'; referenceName: string } & ResourceClassPropertyMetadata) + | ({ + kind: 'objectReference' + referenceName: string + } & ResourceClassPropertyMetadata) + | ({ + kind: 'listReference' + referenceName: string + } & ResourceClassPropertyMetadata) interface ResourceClassPropertyMetadata { name: string @@ -109,8 +115,12 @@ export const createResourceModel = (blueprint: Blueprint): ResourceModel => { currentResourceName = name localClassNames.set(name, []) const sourceResource = - blueprint.resources.find((resource) => resource.resourceType === resourceType) ?? - (resourceType === 'event' ? blueprint.events[0] : blueprint.actionAttempts[0]) + blueprint.resources.find( + (resource) => resource.resourceType === resourceType, + ) ?? + (resourceType === 'event' + ? blueprint.events[0] + : blueprint.actionAttempts[0]) addClass( name, baseResources.get(resourceType) ?? [], diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 974a6ecf..f18f155a 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -102,7 +102,8 @@ export const routes = ( const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { const { response } = endpoint - const responseKey = response.responseType === 'void' ? '' : response.responseKey + const responseKey = + response.responseType === 'void' ? '' : response.responseKey // Batch responses have no single resource type; they deserialize into the // Batch resource. A response whose resource type the blueprint cannot @@ -126,17 +127,17 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { isDeprecated: endpoint.isDeprecated, deprecationMessage: endpoint.deprecationMessage, parameters: endpoint.request.parameters.map((parameter) => ({ - name: parameter.name, - type: getPhpType(parameter), - description: parameter.description, - required: parameter.isRequired, - // The primary identifier of a get endpoint always sorts first in the - // method signature. - position: - endpoint.name === 'get' && parameter.name === `${responseKey}_id` - ? 0 - : undefined, - })), + name: parameter.name, + type: getPhpType(parameter), + description: parameter.description, + required: parameter.isRequired, + // The primary identifier of a get endpoint always sorts first in the + // method signature. + position: + endpoint.name === 'get' && parameter.name === `${responseKey}_id` + ? 0 + : undefined, + })), returnPath: responseKey, returnResource: resourceType === '' ? '' : pascalCase(resourceType), isArrayResponse: From cddf0289a4bd30387be60851cb15255ecf3c7707 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 17:17:05 +0000 Subject: [PATCH 5/7] docs: Document development, testing and publishing Replace the development setup section, which referenced yarn and invoked PHPUnit by path, with the Composer script tasks, the supported PHP versions and how a release is cut. Record why the package version lives in package.json: Composer has no canonical place for it, Packagist derives it from the git tag, and Seam\Utils\PackageVersion reads the field at runtime. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RFK47311nWtbd3gHmjdknR --- README.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2ca38f9f..ee07c3ee 100644 --- a/README.md +++ b/README.md @@ -163,15 +163,86 @@ If you want to install our previous handwritten version, run: `composer require seamapi/seam:1.1` -## Development Setup +## Development and Testing -1. Run `yarn install` to get prettier installed for formatting -2. Install [composer](https://getcomposer.org/). -3. Run `composer install` in this directory -4. Run `composer exec phpunit tests` +### Quickstart -> To run a specific test file, do `composer exec phpunit tests/MyTest.php` +Install [PHP](https://www.php.net/) 8.0 or later, +[Composer](https://getcomposer.org/) and [Node.js](https://nodejs.org/), +then run + +``` +$ git clone git@github.com:seamapi/php.git +$ cd php +$ composer install +$ npm install +``` + +Primary development tasks are defined as [Composer scripts](https://getcomposer.org/doc/articles/scripts.md) +in `composer.json` and available via `composer`. +View them with + +``` +$ composer run-script --list +``` + +| Task | Command | +| ----------------- | ------------------ | +| Run the tests | `composer test` | +| Lint | `composer lint` | +| Format | `npm run format` | +| Build the package | `composer build` | +| Generate the SDK | `npm run generate` | + +Formatting is handled by [Prettier](https://prettier.io/) via +[@prettier/plugin-php](https://github.com/prettier/plugin-php), +so PHP, TypeScript, JSON, YAML and Markdown are all formatted by +`npm run format`. ### Running Tests -You'll need to export `SEAM_API_KEY` to a sandbox workspace API key. \ No newline at end of file +Run the full suite with + +``` +$ composer test +``` + +To run a specific test file, pass it as an argument + +``` +$ composer test -- tests/MyTest.php +``` + +PHPUnit is configured in `phpunit.xml.dist`. + +### Requirements + +This package supports PHP 8.0 and later. +Continuous integration exercises the latest two releases, PHP 8.4 and 8.5. + +### Publishing + +#### Automatic + +New versions are released automatically from `main` by the +[Semantic Release](.github/workflows/semantic-release.yml) workflow, +which reads [Conventional Commits](https://www.conventionalcommits.org/) +and dispatches the [Version](.github/workflows/version.yml) workflow. + +#### Manual + +Run the [Version](.github/workflows/version.yml) workflow with the +version to cut. +It runs `npm version`, which bumps the `version` field in `package.json`, +creates a signed `v*` git tag and pushes it. +Pushing the tag triggers the [Publish](.github/workflows/publish.yml) +workflow, and [Packagist](https://packagist.org/packages/seamapi/seam) +picks up the new tag from its GitHub webhook. + +> Composer has no canonical place to store a package version, since Packagist +> derives it from the git tag. +> This repository therefore keeps the version in `package.json` and lets +> `npm version` manage the tag. +> The SDK also reads that field at runtime via `Seam\Utils\PackageVersion` +> to set its `seam-sdk-version` header, so `package.json` must remain in the +> published package. From 04251367aeb57da6df904859d11a67ac5e455c39 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 18:47:24 +0000 Subject: [PATCH 6/7] ci: Test against the supported PHP floor and ceiling Run the check matrix on PHP 8.0 and 8.5 rather than the latest two releases, so continuous integration covers both ends of the range the package declares support for. Default the setup action and the build workflow to 8.5, matching the top of the matrix as the Ruby and Python SDKs do. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RFK47311nWtbd3gHmjdknR --- .github/actions/setup/action.yml | 2 +- .github/workflows/_build.yml | 2 +- .github/workflows/check.yml | 6 +++--- README.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 0943da63..fd9fcf70 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -6,7 +6,7 @@ inputs: php_version: description: The PHP version. required: false - default: '8.4' + default: '8.5' install_dependencies: description: Install dependencies. required: false diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml index 31d46310..63aea632 100644 --- a/.github/workflows/_build.yml +++ b/.github/workflows/_build.yml @@ -8,7 +8,7 @@ on: description: The PHP version. type: string required: false - default: '8.4' + default: '8.5' outputs: artifact_name: description: The artifact name. diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 60d0be57..95ca05d1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,7 +20,7 @@ jobs: os: - ubuntu-latest php: - - '8.4' + - '8.0' - '8.5' include: - os: ubuntu-latest @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: php: - - '8.4' + - '8.0' - '8.5' steps: - name: Checkout @@ -73,7 +73,7 @@ jobs: os: - ubuntu-latest php: - - '8.4' + - '8.0' - '8.5' include: - os: ubuntu-latest diff --git a/README.md b/README.md index ee07c3ee..a1297e55 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ PHPUnit is configured in `phpunit.xml.dist`. ### Requirements This package supports PHP 8.0 and later. -Continuous integration exercises the latest two releases, PHP 8.4 and 8.5. +Continuous integration exercises both ends of that range, PHP 8.0 and 8.5. ### Publishing From ec1e185fee4963c1a93c7be290d18ab7a41ae873 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 20:33:16 +0000 Subject: [PATCH 7/7] fix: Stop publishing package.json and inject the version instead Seam\Utils\PackageVersion read package.json from the package root at runtime to set the seam-sdk-version header, which forced a development manifest into every published package. Replace it with a VERSION constant, and inject that constant from package.json in the version lifecycle script, which npm runs after the bump but before the commit, so the value is part of the tagged commit. Packagist publishes the tag with no build step in between, so unlike the JavaScript SDK, which injects at pack time, the version has to be committed before the tag, as the Ruby SDK does with lib/seam/version.rb. Add export-ignore rules to .gitattributes so development files are kept out of the archives Composer downloads as dist, which composer.json's archive exclude list does not govern, and exclude package.json from composer archive as well. Cover the constant with tests asserting it matches package.json and is what SeamClient sends, so a hand edit or a missed injection fails. Removes Seam\Utils\PackageVersionException, which only signalled an unreadable package.json and has no remaining purpose. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RFK47311nWtbd3gHmjdknR --- .gitattributes | 22 ++++++++++++++++++ README.md | 24 ++++++++++++++------ composer.json | 18 ++++++++------- package.json | 1 + src/Utils/PackageVersion.php | 43 ++++++++--------------------------- tests/PackageVersionTest.php | 36 +++++++++++++++++++++++++++++ tsconfig.json | 2 +- version.ts | 44 ++++++++++++++++++++++++++++++++++++ 8 files changed, 141 insertions(+), 49 deletions(-) create mode 100644 tests/PackageVersionTest.php create mode 100644 version.ts diff --git a/.gitattributes b/.gitattributes index cadd4117..641e8a3b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,25 @@ src/Resources/*.php linguist-generated src/Routes/*.php linguist-generated src/SeamClient.php linguist-generated + +# Keep development files out of the published package. +# GitHub builds the archives Composer downloads as dist with git archive, +# which honours these rules. +/.devcontainer export-ignore +/.editorconfig export-ignore +/.env.example export-ignore +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/.npmrc export-ignore +/.prettierignore export-ignore +/.prettierrc.json export-ignore +/.releaserc.json export-ignore +/codegen export-ignore +/eslint.config.ts export-ignore +/package-lock.json export-ignore +/package.json export-ignore +/phpunit.xml.dist export-ignore +/tests export-ignore +/tsconfig.json export-ignore +/version.ts export-ignore diff --git a/README.md b/README.md index a1297e55..567a864c 100644 --- a/README.md +++ b/README.md @@ -234,15 +234,25 @@ and dispatches the [Version](.github/workflows/version.yml) workflow. Run the [Version](.github/workflows/version.yml) workflow with the version to cut. It runs `npm version`, which bumps the `version` field in `package.json`, -creates a signed `v*` git tag and pushes it. +injects that version into `Seam\Utils\PackageVersion`, creates a signed `v*` +git tag and pushes it. Pushing the tag triggers the [Publish](.github/workflows/publish.yml) workflow, and [Packagist](https://packagist.org/packages/seamapi/seam) picks up the new tag from its GitHub webhook. > Composer has no canonical place to store a package version, since Packagist -> derives it from the git tag. -> This repository therefore keeps the version in `package.json` and lets -> `npm version` manage the tag. -> The SDK also reads that field at runtime via `Seam\Utils\PackageVersion` -> to set its `seam-sdk-version` header, so `package.json` must remain in the -> published package. +> derives it from the git tag, and it publishes the tag as-is with no build +> step in between. +> This repository therefore keeps the version in `package.json`, which is a +> development manifest that is not published, and injects it into the +> `Seam\Utils\PackageVersion::VERSION` constant used for the +> `seam-sdk-version` header. +> +> The injection runs from `version.ts`, wired to the `version` lifecycle +> script, which npm runs after the bump but before the commit, so the +> updated constant is part of the tagged commit. +> Never edit that constant by hand; a test asserts it matches `package.json`. + +Development files are kept out of the published package with `export-ignore` +rules in `.gitattributes`, which `git archive` honours when GitHub builds the +archives Composer downloads as `dist`. diff --git a/composer.json b/composer.json index 7e4c0eab..6827b5ce 100644 --- a/composer.json +++ b/composer.json @@ -54,24 +54,26 @@ "archive": { "exclude": [ "/.devcontainer", - "/.github", - "/codegen", - "/node_modules", - "/pkg", - "/tests", - "/tmp", - "/vendor", "/.editorconfig", "/.env.example", + "/.github", "/.npmrc", "/.phpunit.result.cache", "/.prettierignore", "/.prettierrc.json", "/.releaserc.json", + "/codegen", "/eslint.config.ts", + "/node_modules", "/package-lock.json", + "/package.json", "/phpunit.xml.dist", - "/tsconfig.json" + "/pkg", + "/tests", + "/tmp", + "/tsconfig.json", + "/vendor", + "/version.ts" ] }, "scripts": { diff --git a/package.json b/package.json index d220fa7c..e2b1f75c 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "private": true, "license": "MIT", "scripts": { + "version": "tsx ./version.ts && git add src/Utils/PackageVersion.php", "postversion": "git push --follow-tags", "generate": "tsx codegen/smith.ts", "postgenerate": "npm run format", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index 0dc8a6a3..4aa2fb50 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -2,41 +2,18 @@ namespace Seam\Utils; -class PackageVersionException extends \Exception {} - class PackageVersion { - public static function get() + /** + * The version of this package. + * + * Injected from package.json when a version is cut, by the version + * lifecycle script in package.json. Do not edit by hand. + */ + public const VERSION = "3.5.0"; + + public static function get(): string { - $filePath = __DIR__ . "/../../package.json"; - - if (!file_exists($filePath)) { - throw new PackageVersionException( - "Can't get package version. File package.json does not exist.", - ); - } - - $content = file_get_contents($filePath); - if ($content === false) { - throw new PackageVersionException( - "Unable to read package.json file to get package version.", - ); - } - - $json = json_decode($content, true); - if (json_last_error() !== JSON_ERROR_NONE) { - throw new PackageVersionException( - "JSON decode error occurred when decoding package.json: " . - json_last_error_msg(), - ); - } - - if (!isset($json["version"])) { - throw new PackageVersionException( - "Version not set in package.json", - ); - } - - return $json["version"]; + return self::VERSION; } } diff --git a/tests/PackageVersionTest.php b/tests/PackageVersionTest.php new file mode 100644 index 00000000..d45e970d --- /dev/null +++ b/tests/PackageVersionTest.php @@ -0,0 +1,36 @@ +assertIsString($contents, "Could not read $path"); + + $package = json_decode($contents, true); + $this->assertIsArray($package, "Could not decode $path"); + $this->assertArrayHasKey("version", $package); + + $this->assertSame( + $package["version"], + PackageVersion::get(), + "Seam\\Utils\\PackageVersion is out of date with package.json. " . + "It is injected by the version lifecycle script when a " . + "version is cut and should not be edited by hand.", + ); + } + + public function testVersionIsUsedAsTheSdkVersionHeader(): void + { + $seam = new \Seam\SeamClient("seam_apikey1_token"); + $headers = $seam->client->getConfig("headers"); + + $this->assertSame(PackageVersion::get(), $headers["seam-sdk-version"]); + } +} diff --git a/tsconfig.json b/tsconfig.json index 6190bfbf..c3d380f1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,5 +2,5 @@ "$schema": "https://json.schemastore.org/tsconfig.json", "extends": "./node_modules/@seamapi/smith/tsconfig.base.json", "files": ["codegen/index.ts"], - "include": ["codegen/**/*", "eslint.config.ts"] + "include": ["codegen/**/*", "eslint.config.ts", "version.ts"] } diff --git a/version.ts b/version.ts new file mode 100644 index 00000000..697bea44 --- /dev/null +++ b/version.ts @@ -0,0 +1,44 @@ +import { readFile, writeFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' + +const versionFile = './src/Utils/PackageVersion.php' + +const versionPattern = /public const VERSION = "[^"]*";/ + +const main = async (): Promise => { + const version = await injectVersion( + fileURLToPath(new URL(versionFile, import.meta.url)), + ) + // eslint-disable-next-line no-console + console.log(`✓ Version ${version} injected into ${versionFile}`) +} + +const injectVersion = async (path: string): Promise => { + const { version } = await readPackageJson() + + if (version == null) { + throw new Error('Missing version in package.json') + } + + const data = (await readFile(path)).toString() + + if (!versionPattern.test(data)) { + throw new Error(`Could not find the version constant in ${versionFile}`) + } + + await writeFile( + path, + data.replace(versionPattern, `public const VERSION = "${version}";`), + ) + + return version +} + +const readPackageJson = async (): Promise<{ version?: string }> => { + const pkgBuff = await readFile( + fileURLToPath(new URL('package.json', import.meta.url)), + ) + return JSON.parse(pkgBuff.toString()) +} + +await main()