Conversation
Adds the platform.build config tree (identity, PHP build settings, exclude paths, boot hooks) that the platform:build command reads in later tasks, plus a BuildConfigBuilder for the PHP config format. Renames PlatformConfigBuilder::build() to toArray() so build() can open the new section, matching security() and the other section accessors; updates every caller and documents the break in UPGRADE.md.
TenantCreationVoter injects TenantLock, which MULTI_TENANCY_SERVICES already removes when multi-tenancy is disabled (the default). The voter itself was missing from that list, so the container kept a service whose argument no longer existed and failed to compile. Also ignore the throwaway test-app/ directory created by scripts/test-app.sh.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Several correctness issues affect command registration, runtime paths, environment handling, and documented application hooks.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds reusable single-executable FrankenPHP builds for platform applications, including configuration, archiving, static compilation, embedded app runtime, documentation, and related tenancy/container fixes.
Changes:
- Adds
platform:build/platform:compileand build configuration. - Adds Go/FrankenPHP build assets, preflight checks, archiving, and runtime support.
- Updates schema, documentation, dependencies, tests, and Rector configuration.
File summaries
| File | Description |
|---|---|
tests/Bundle/PlatformBundle/DependencyInjection/SolidWorxPlatformExtensionTest.php |
Build configuration and tenancy tests |
tests/Bundle/PlatformBundle/Console/CommandTest.php |
Command IO lifecycle tests |
tests/Bundle/PlatformBundle/Config/PlatformConfigurationTest.php |
Build defaults validation |
tests/Bundle/PlatformBundle/Config/Builder/BuildConfigBuilderTest.php |
Fluent build configuration tests |
tests/Bundle/PlatformBundle/Command/BuildCommandTest.php |
Build command behavior tests |
tests/Bundle/PlatformBundle/Build/VersionResolverTest.php |
Version resolution tests |
tests/Bundle/PlatformBundle/Build/StaticBuilderTest.php |
Static builder tests |
tests/Bundle/PlatformBundle/Build/PreflightTest.php |
Preflight validation tests |
tests/Bundle/PlatformBundle/Build/BuildOptionsTest.php |
Build option resolution tests |
tests/Bundle/PlatformBundle/Build/AppArchiverTest.php |
Archive behavior tests |
src/Bundle/Platform/Resources/config/services.php |
Registers build services |
src/Bundle/Platform/Resources/build/xcaddy |
FrankenPHP build shim |
src/Bundle/Platform/Resources/build/UPSTREAM.md |
Upstream synchronization notes |
src/Bundle/Platform/Resources/build/untar.go |
Embedded archive extraction |
src/Bundle/Platform/Resources/build/logger.go |
Caddy logger adapter |
src/Bundle/Platform/Resources/build/internal/serverconfig/server_name.go |
Server-name construction |
src/Bundle/Platform/Resources/build/internal/serverconfig/server_name_test.go |
Server-name tests |
src/Bundle/Platform/Resources/build/go.mod |
Go build dependencies |
src/Bundle/Platform/Resources/build/build-static.sh |
Vendored static build script |
src/Bundle/Platform/Resources/build/app.go |
Embedded application runtime |
src/Bundle/Platform/DependencyInjection/SolidWorxPlatformExtension.php |
Build parameter resolution and tenancy fix |
src/Bundle/Platform/Console/Command.php |
Corrects command IO initialization |
src/Bundle/Platform/Config/PlatformConfiguration.php |
Adds build configuration tree |
src/Bundle/Platform/Config/Builder/PlatformConfigBuilder.php |
Adds binary build builder entry point |
src/Bundle/Platform/Config/Builder/BuildConfigBuilder.php |
Fluent build configuration builder |
src/Bundle/Platform/Command/BuildCommand.php |
Build command orchestration |
src/Bundle/Platform/Build/VersionResolver.php |
Resolves binary versions |
src/Bundle/Platform/Build/StaticBuilder.php |
Runs static compilation |
src/Bundle/Platform/Build/Severity.php |
Preflight severity enum |
src/Bundle/Platform/Build/Problem.php |
Preflight problem model |
src/Bundle/Platform/Build/Preflight.php |
Tool and application checks |
src/Bundle/Platform/Build/BuildStepFailedException.php |
Build failure exception |
src/Bundle/Platform/Build/BuildOptions.php |
Resolved build options |
src/Bundle/Platform/Build/Archive.php |
Archive metadata model |
src/Bundle/Platform/Build/AppArchiver.php |
Application tarball creation |
rector.php |
Excludes unsafe Rector rules |
platform-schema.json |
Adds build schema |
docs/index.md |
Documents build section |
docs/configuration/index.md |
Documents build configuration |
docs/build/index.md |
Adds build guide |
composer.json |
Adds filesystem/process dependencies |
.gitignore |
Ignores build artifacts |
Review details
- Files reviewed: 41/43 changed files
- Comments generated: 8
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| use function str_contains; | ||
|
|
||
| #[AsCommand( | ||
| name: 'platform:build|platform:compile', |
|
|
||
| // Only set if not already set | ||
| for key, value := range envVars { | ||
| if os.Getenv(key) == "" { |
| // phpincludedir above also points into source. Upstream's cleanup assumes a single run; | ||
| // left inherited from a real CI environment (our own documented CI usage sets one), the | ||
| // second run would fail. RELEASE is neutralised the same way, for the same reason. | ||
| $environment['CI'] = ''; |
|
|
||
| $filesystem = new Filesystem(); | ||
| $filesystem->mkdir($options->outputDir); | ||
| $filesystem->rename($produced, $destination, overwrite: true); |
| if command == "" { | ||
| continue | ||
| } | ||
| if err := runConsoleCommand(command); err != nil { |
| runCmd.PersistentFlags().StringVar(&sslKeyFile, "ssl-key", "", "Path to custom SSL private key file (requires --ssl-cert and --domain)") | ||
| runCmd.PersistentFlags().BoolVar(&enableWorkerMode, "worker-mode", false, "Enable FrankenPHP worker mode for improved performance (keeps PHP workers alive between requests). Recommended for SaaS/high-traffic deployments. Can also be enabled via FRANKENPHP_WORKER_MODE=1 environment variable") | ||
| runCmd.PersistentFlags().IntVar(&workerThreads, "worker-threads", 2, "Number of FrankenPHP worker threads when worker mode is enabled (default: 2)") | ||
| runCmd.PersistentFlags().IntVar(&messengerWorkers, "messenger-workers", 1, fmt.Sprintf("Number of messenger worker processes to spawn. Each worker processes async messages independently. Set to 0 to disable built-in workers entirely (recommended for Kubernetes, where a dedicated worker pod runs '%s worker'). Increase above 1 for high-traffic standalone deployments (e.g., --messenger-workers=5)", filepath.Base(os.Args[0]))) |
|
|
||
| if [ "${CURRENT_OS}" = "linux" ]; then | ||
| # musl static build: GNU ld wants a dynamic symbol list and a static PIE. | ||
| EXTLDFLAGS="-static-pie -Wl,-z,stack-size=0x80000 -Wl,--dynamic-list=${DYNSYM}" |
| var urls string | ||
| var domainNote string | ||
|
|
||
| if len(domain) > 0 { |
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.
Adds
bin/console platform:build(aliasplatform:compile), which compiles any application built on the platform into a single static executable — a FrankenPHP/Caddy web server, the Symfony app embedded as a tarball, and messenger consumers, in one binary.The capability previously lived in SolidInvoice as four bash scripts plus a Go program, all wired to that one application. It now lives here, reusable by any platform app.
How it works
PHP owns configuration, preflight checks, archiving and orchestration. The Go program and
build-static.shship verbatim insrc/Bundle/Platform/Resources/build/and are never rewritten — application identity reaches the binary throughgo build -ldflags -X, applied by a shell shim that impersonatesxcaddy. That keeps the upstream build script byte-identical and re-syncable (verified: it still diffs clean against upstream).Configuration lives under
platform.build; seedocs/build/index.md.Scope
Builds are host-only —
build-static.shhas no cross-compile path, so there is no--os/--arch. The archive is the checkout as it stands: the command verifies and warns, but does not runcomposer installor build assets.Note on scope of this branch
This branch also carries 7 commits of tenant-scope work from an earlier session that were never pushed. They are included here rather than split out.
Not yet done
No real build has been run. A first build downloads and compiles PHP and takes 30–60 minutes. Before merging, one end-to-end run is worth doing, checking what the binary's
versioncommand prints — the Go linker silently ignores an-Xflag with an unknown target symbol, so wrong ldflags wiring yields a working binary with wrong branding that an exit-code check cannot see.Known risks for that first run are recorded in the branch history: static-php-cli receives
--with-frankenphp-appunconditionally (hence EMBED being a bounded extracted copy rather than the project root), and the default extension set is larger than documented — see below.Upstream bug found
build-static.shgates composer-derived extensions on${EMBED}/vendor/installed.json, but Composer 2 writesvendor/composer/installed.json. That branch is therefore dead and every build falls back to the script's ~69-extension default list. The script stays byte-identical by design, so this is documented rather than patched — seesrc/Bundle/Platform/Resources/build/UPSTREAM.md.Also in this branch
TenantCreationVoterinjectedTenantLockwhile only the latter was removed with multi-tenancy disabled — the default. Any app without multi-tenancy could not boot.PlatformConfigBuilder::build()→toArray(), a BC break that should not have happened; the build section accessor isbinaryBuild()instead.Verification
595 tests / 1661 assertions · PHPStan max clean · ECS + Rector clean ·
gofmtclean, Go internal tests pass.