feat: upgrade API Platform to 5.0, Mercure to 1.0 and dependencies to latest stable - #691
Open
vincentchalamon wants to merge 9 commits into
Open
vincentchalamon wants to merge 9 commits into
vincentchalamon wants to merge 9 commits into
Conversation
The FrankenPHP builder image ships Go 1.26.8 and pins GOTOOLCHAIN=local,
which forbids downloading a newer toolchain. github.com/dunglas/mercure/caddy
v1.0.0 declares `go 1.27` in its go.mod, so xcaddy fails:
go: github.com/dunglas/mercure/caddy@v1.0.0 requires go >= 1.27
(running go 1.26.8; GOTOOLCHAIN=local)
This breaks every Docker build on every branch since Mercure 1.0.0 was
released. Bumping the builder image is not an option, 1.12.7 is the latest
FrankenPHP release and it is the one shipping Go 1.26.8.
Restore the Go default so the toolchain a module asks for is fetched on
demand.
vincentchalamon
force-pushed
the
fix/go-toolchain
branch
2 times, most recently
from
September 18, 2026 14:46
2e48946 to
c0ad26c
Compare
Mercure 1.0 replaces the flat publisher_jwt/subscriber_jwt directives with issuer blocks and expects RFC 9068 access tokens: iss, aud, exp and a typ of at+jwt, verified against a declared issuer. The old directives now only work in a compatibility mode that drops those checks and re-accepts the token in the query string, so migrate rather than opt into it. - Caddyfile: declare an issuer with publisher and subscriber verifiers, and pin resource_identifier. The API publishes through the internal MERCURE_URL while tokens are minted for the public URL, so without pinning, the hub derives a different audience per request and rejects them. The value is substituted at parse time, since the hub validates it as a URL before runtime placeholders resolve. - mercure.yaml: protocol_version 1.0, which makes symfony/mercure-bundle mint RFC 9068 tokens, plus the iss, sub and client_id claims it then requires. - The demo directive is now playground, and its UI moved from /.well-known/mercure/ui/ to /.well-known/mercure/debug/. Subscribers are unaffected: the PWA subscribes anonymously, with no token.
The test helpers moved: ApiPlatform\Symfony\Bundle\Test\{ApiTestCase,Client}
are deprecated in favour of ApiPlatform\Test\{ApiTestCase,Client}, now shipped
by a separate api-platform/test package. The deprecated shims extend classes
from that package, so without it the test suite fatals rather than warns.
That package supports phpunit ^11.5 || ^12.2, so PHPUnit moves from 13 to 12.
It is the one dependency that cannot be on its latest major while API Platform
5.0 is.
Nothing else in the demo needed changing: PHPStan reports no error and no use
of a deprecated API, and Rector has nothing to propose.
@types/node moves to 26, the rest are patch and minor bumps. Two majors are held back, both blocked by the ecosystem rather than by the demo: - typescript stays on 6.x. typescript-eslint refuses to load under TS 7 and tracks support for >= 7.1 in typescript-eslint/typescript-eslint#10940. - eslint stays on 9.x. eslint-plugin-react 7.37.5, pulled in transitively by eslint-config-next, still uses the context API that ESLint 10 removed, and crashes on the first React file. Verified in a clean container: pnpm lint reports no error, pnpm build passes.
Mercure 1.0 renamed the subscribe query parameter: "topic" is rejected with
unknown topic matcher query parameter: "topic" (use "match" or "match_urlpattern")
Publishing is unaffected, the publish endpoint still reads "topic" form fields.
This was only caught because the CI reachability check happens to subscribe.
The PWA builds its EventSource URL the same way, so real-time updates would
have shipped broken.
vincentchalamon
force-pushed
the
fix/go-toolchain
branch
from
September 18, 2026 15:23
cfdb9b9 to
750ca4a
Compare
API Platform 5.0 no longer appends "; charset=utf-8" to JSON media types. Per RFC 8259 and the RFC 6839 "+json" suffix, JSON defines no charset and is always UTF-8, so sending the parameter can break strict clients; only text/* and application/xml keep it. That accounted for all 80 functional failures. The Mercure debugger also moved from /.well-known/mercure/ui/ to /.well-known/mercure/debug/.
MERCURE_PUBLISHER_JWT_ALG and MERCURE_SUBSCRIBER_JWT_ALG are set nowhere in the project. The hub defaults a raw secret to HS256, which is what symfony/mercure-bundle signs with, so the argument only carried an empty placeholder.
Two deliberate changes in the major: - An unauthenticated request now reports "Access Denied." as its description rather than the Symfony entry point message, mirroring "detail". - Hydra "range" is a plain IRI string for most properties and only a list for those carrying an owl:equivalentClass restriction, so the documentation test has to accept both shapes. Four failures remain, all on the same validation message template not being interpolated; they are tracked separately rather than aligned to the buggy output.
Mercure 1.0 split the old "demo" directive in two. "playground" does more than
serve the debugger UI: it also redirects the site root to it.
if m.Playground && r.URL.Path == "/" {
http.Redirect(w, r, defaultHubURL+"/debug/", http.StatusFound)
The hub is mounted inside the API, which owns "/", so the entrypoint answered
with the debugger HTML instead of the JSON-LD entrypoint, and every client
introspecting the API broke.
"debugger" serves the same UI at /.well-known/mercure/debug/ without touching
the root.
Contributor
Author
|
Status: waiting for api-platform/api-doc-parser#181 Status: waiting for api-platform/core#8546 |
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.
Docker builds have been failing on every branch since Mercure 1.0.0 was released. Rather than pin around it, this upgrades API Platform to 5.0, Mercure to 1.0, and the remaining dependencies to their latest stable, majors included.
Unbreaking the build
dunglas/frankenphp:1.12.7-builderships Go 1.26.8 and pinsGOTOOLCHAIN=local, which forbids downloading a newer toolchain. Mercure Caddy 1.0.0 declaresgo 1.27, soxcaddy buildaborted:1.12.7is the latest FrankenPHP release, so restoring the Go default is the fix. It also prevents the next module that raises its minimum from breaking the build again.Mercure 1.0
1.0 replaces the flat
publisher_jwt/subscriber_jwtdirectives with issuer blocks, and expects RFC 9068 access tokens:iss,aud,expand atypofat+jwt, verified against a declared issuer. The old directives still work, but only in a compatibility mode that, in upstream words, "drops the required exp, the audience check, the at+jwt typ check and the verified-issuer check, and re-accepts the access token in the URL query string", and additionally needs thedeprecated_topicanddeprecated_claimbuild tags. That is three switches to import a major and disable most of it, so this migrates instead.Caddyfile: anissuerblock with publisher and subscriber verifiers, plus a pinnedresource_identifier. The API publishes through the internalMERCURE_URLwhile tokens are minted for the public URL, so left unpinned the hub derives a different audience per request and rejects them. The value is substituted at parse time, because the hub validates it as a URL before runtime placeholders resolve.mercure.yaml:protocol_version: 1.0, which makessymfony/mercure-bundlemint RFC 9068 tokens, plus theiss,subandclient_idclaims it then requires.demodirective is nowplayground, and its UI moved from/.well-known/mercure/ui/to/.well-known/mercure/debug/.Subscribers are unaffected: the PWA subscribes anonymously, with no token.
API Platform 5.0
ApiPlatform\Symfony\Bundle\Test\{ApiTestCase,Client}are deprecated in favour ofApiPlatform\Test\{ApiTestCase,Client}, now shipped by a separateapi-platform/testpackage. The deprecated shims extend classes from that package, so without it the suite fatals rather than warns, which is also why Rector briefly proposed deleting sevensetUp()methods: it could no longer resolve the parent class. Those proposals were a symptom, not a change, and are gone now that the package is installed.Nothing else in the demo needed touching. PHPStan reports no error and no use of a deprecated API, Rector has nothing to propose, and the 27 unit tests pass with
failOnDeprecationactive.Dependencies held back
Three, all blocked by the ecosystem rather than by the demo:
phpunit/phpunitapi-platform/test5.0.0 supports^11.5 || ^12.2; there is no release supporting 13typescripteslinteslint-plugin-react7.37.5, pulled transitively byeslint-config-next, uses the context API ESLint 10 removed and crashes on the first React filePostgreSQL is left alone in the Helm chart as requested.
external-dnsand Redis are already on their latest. GitHub Actions are handled by Renovate on its monthly schedule.Verification
Run locally before pushing:
frankenphp validateagainst the real Caddyfile, which provisions the modules rather than only parsing: valid. The same command reproduces the exact CI failure when the issuer block is reverted.mercure.yaml.phpstan analyse: no errors.rector process --dry-run: nothing to change.phpunit --testsuite Unit: 27 tests, 114 assertions, green.pnpm lintreports no error,pnpm buildpasses.helm lintpasses.Functional and E2E suites need a database, so CI is the arbiter there.
Closes #692