fix: Improve CI/CD reliability and add static analysis - #18
Merged
Merged
Conversation
…dge step - Add fetch-depth: 0 to checkout so semantic-release has full git history to correctly determine the next version - Replace php-actions/composer with the same composer install pattern used in ci.yaml for consistency; use proper cache key per PHP version - Remove the coverage badge generation step from the release pipeline; it was a fragile side-effect that could block releases on gh-pages push failures (will be moved to ci.yaml) - Add explicit extensions and xdebug coverage config to setup-php
The badge step was previously in release.yaml, where a push failure to gh-pages could block a release entirely. It now lives as a dedicated 'coverage-badge' job in ci.yaml that: - only runs on pushes to master (not PRs), preventing concurrent gh-pages write conflicts - depends on unit-tests passing, so the badge always reflects a green build - uses the same composer install pattern as the rest of ci.yaml
Previously release notes only existed in GitHub Releases; CHANGELOG.md was never written to the repo. This adds: - @semantic-release/changelog: writes/updates CHANGELOG.md on each release - @semantic-release/git: commits CHANGELOG.md back to master with a [skip ci] message to avoid triggering another release run Also adds explicit `permissions: contents/issues/pull-requests: write` to the release job, required for the git plugin to push back to the branch, and installs the extra plugins via cycjimmy extra_plugins.
- Add phpstan/phpstan ^1.12 as a dev dependency (^1.x supports PHP 7.2+, keeping compatibility with the library's minimum PHP 7.3 requirement) - Add phpstan.neon config targeting src/ at level 6 - Add 'phpstan' target to Makefile - Add 'static-analysis' job to ci.yaml that runs in parallel with unit-tests (both depend on dependency-validation), using a single PHP 8.3 runner — no need to matrix static analysis across versions
- KeyInterface: tighten __toString() return to string (was bool|string) - AbstractKey: initialise $kty and $alg to '' to satisfy string return types; cast json_encode() result in __toString(); add is_array() guard before iterating json_decode() result in createFromJSON() - Rsa: initialise $n and $e to ''; add is_array() guard in createFromJSON() - Base64UrlConverterInterface: add bool type hint to $strict parameter - Base64UrlConverter: throw on false return from base64_decode() instead of silently returning false as string - KeyConverter: extract base64_decode() result into a variable and throw on false before passing to BigInteger - KeyFactory: add null-checks on openssl_pkey_get_public() and openssl_pkey_get_details() return values; guard json_encode() result - KeySet: fix __toString() return type; use ?? '' for nullable getKeyId() array key; add return type annotation to jsonSerialize() - KeySetFactory: guard json_encode($keyData) result before passing to createFromJson() - composer.lock: update to include phpstan/phpstan ^1.12
Upgrade phpstan/phpstan from ^1.12 to ^2.2 and fix all 8 reported errors: - AbstractKey::createFromJSON: remove new static() by making $prototype required (non-nullable); callers must always supply a concrete instance, eliminating the unsafe instantiation of an abstract class - Rsa::createFromJSON: narrow return type from KeyInterface to self; always construct a new self() prototype when none is provided, removing the last new static() call; add @var self cast after parent call so PHPStan tracks the concrete type through the loop - KeyFactory::createFromPem: add @PARAM array<string, mixed> to $options - KeySet: add @implements IteratorAggregate<int, KeyInterface> on the class; add @return ArrayIterator<int, KeyInterface> to getIterator(); remove the unused $keyFactory property, constructor injection, and setter (KeySet never used KeyFactory directly — that was KeySetFactory's responsibility) - composer.lock: update for phpstan/phpstan ^2.2
…ency-validation The php-actions/composer@v6 action fails with a git safe.directory ownership error when run in GitHub Actions. Replace it with the same composer install pattern used by all other jobs in this workflow (shivammathur/setup-php + explicit cache dir + composer install). Also update composer.lock to resolve phpstan/phpstan to 2.2.12, matching the ^2.2 constraint added in the previous commit.
…ibility The previous lock file was generated by the composer:2 Docker image which runs PHP 8.4 internally. This caused it to resolve symfony/string to v8.1.2 which requires PHP >=8.4.1, breaking composer install on the PHP 8.3 runner in the dependency-validation job. Regenerated on php:8.3-cli, which downgrades symfony/string to v7.4.15 (a transitive dependency of friendsofphp/php-cs-fixer), making the lock file installable on all supported PHP versions in the matrix.
PHP's LSP enforcement requires that overriding methods use covariant return types. Returning `self` (which resolves to `Rsa`) is not compatible with the parent's declared return type of `KeyInterface`. Changing to `static` satisfies the constraint since `static` is the only narrowing return type PHP recognises as covariant in overrides.
…JSON When no prototype is provided, instantiate the concrete subclass via new static() instead of throwing InvalidArgumentException. This allows subclasses (and test mocks) to call createFromJSON without supplying an explicit prototype, while keeping the method safe since static resolves to the concrete type at call time, never to the abstract AbstractKey.
- AbstractKey::createFromJSON: add @return static docblock and @var static annotation on $instance (clone), suppress the new.static PHPStan warning with inline // @PHPStan-Ignore new.static - Rsa::createFromJSON: change declared return type from `static` (PHP 8.0+ only) back to `self` for PHP 7.3/7.4 compatibility, add @return static docblock, change @var self to @var static so PHPStan accepts the covariant return without error
- Update README.md and CONTRIBUTING.md: replace PHP 7.3 with PHP 7.4 as the minimum version requirement, consistent with the PHP 7.3 support drop merged in master (ebfb030) - Regenerate composer.lock on PHP 8.3 after rebase to ensure it reflects the current platform and dependency resolution
Strobotti
force-pushed
the
improve/ci-cd-workflows
branch
from
September 5, 2026 10:33
1dde1f9 to
bcd3ffd
Compare
…ic-analysis job - Change cache key hash from composer.json to composer.lock so the cache is invalidated only when locked dependencies actually change - Replace `composer update` with `composer install` to install the exact versions recorded in composer.lock, matching the other jobs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A set of improvements to the GitHub Actions workflows and source code
quality, addressing several reliability issues in the CI and release
pipelines and introducing PHPStan static analysis.
Changes
Release pipeline (
release.yaml)fetch-depth: 0to the checkout step — semantic-release needsthe full git history to correctly calculate the next version number.
Without it, a shallow clone can produce wrong or missing version bumps.
php-actions/composerwith the standardshivammathur/setup-phpcomposer installpattern, consistent withci.yaml.permissions: contents/issues/pull-requests: write,required for the git plugin to commit back to master.
@semantic-release/changelogand@semantic-release/gitviaextra_pluginsand wire them into.releaserc.json. Previously releasenotes existed only in GitHub Releases;
CHANGELOG.mdwill now bewritten and committed to the repo on every release.
that could block releases on
gh-pagespush failures (moved toci.yaml).CI pipeline (
ci.yaml)release.yamlinto adedicated
coverage-badgejob. The badge only runs on pushes tomaster (not PRs), depends on
unit-testspassing, and can no longerblock a release if the
gh-pagespush fails.static-analysisjob running PHPStan on PHP 8.3, in parallelwith
unit-tests(both depend ondependency-validation). The jobuses
composer.lockas the cache key andcomposer installfor afully reproducible, pinned vendor state.
PHP_CS_FIXER_IGNORE_ENVflag.Static analysis (
phpstan.neon,composer.json,Makefile)phpstan/phpstan ^2.2as a dev dependency.phpstan.neontargetingsrc/at level 6.phpstantarget to theMakefile.Source fixes (
src/)All findings from PHPStan level 6 resolved — no suppressions or
baselines used:
''valuesopenssl_pkey_get_public()andopenssl_pkey_get_details()returnvalues guarded against
falsejson_encode()andbase64_decode()return values guarded againstfalse;Base64UrlConverter::decode()now throws on failure ratherthan silently passing
falseas a string__toString()return types tightened tostringis_array()guards added before iteratingjson_decode()resultsbooltype hint added to$strictparameter inBase64UrlConverterInterface::decode()Testing
Existing test suite is unchanged and should continue to pass across the
full PHP 7.4–8.5 matrix.