[Studio] Ship frontend build as a committed archive (extracted at cache warmup) - #690
Conversation
…he warmup) Adopts the packaged-build approach used in studio-ui-bundle and pimcore-agent-bundle: instead of committing the expanded src/Resources/public/studio/build/ tree, we commit a single content-addressed build-dist/build-<id>.zip and let studio-ui-bundle reconstruct the build directory at cache warmup. This removes the per-build file churn and merge conflicts the "Automatic frontend build" commits used to cause. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Pin the write-enabled workflow to a SHA and correct or configure the archive merge strategy.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Replaces committed expanded Studio frontend assets with a content-addressed archive extracted during cache warmup.
Changes:
- Adds deterministic build IDs and archive packaging.
- Integrates archive extraction into the entry-point provider.
- Updates CI and removes expanded generated assets.
File summaries
| File | Description |
|---|---|
src/Webpack/WebpackEntryPointProvider.php |
Provides and extracts the build archive. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/remoteEntry.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/main.35725457.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/745.56a11b0d.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/67.8469aaea.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/67.8469aaea.js |
Removes generated JavaScript. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/668.1ae87f0f.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/658.b1390ae9.js |
Removes generated JavaScript. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/359.ef7a6289.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/272.5aa86da5.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/272.5aa86da5.js |
Removes generated JavaScript. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/132.c328628b.js |
Removes generated JavaScript. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/async/__federation_expose_default_export.619777ef.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/static/js/109.4563ef16.js.LICENSE.txt |
Removes generated license output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/mf-stats.json |
Removes federation statistics. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/mf-manifest.json |
Removes the federation manifest. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/manifest.json |
Removes the expanded asset manifest. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/main.html |
Removes expanded HTML output. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/exposeRemote.js |
Removes the expanded remote bootstrap. |
src/Resources/public/studio/build/405a7c51-e8c7-4dda-81a4-aca2bbebbe7f/entrypoints.json |
Removes expanded entry-point metadata. |
src/Resources/public/studio/.gitkeep |
Retains the extraction parent directory. |
assets/studio/rsbuild.config.ts |
Generates deterministic build IDs. |
assets/studio/package.json |
Adds the archive-packaging command. |
.gitignore |
Ignores extracted build output. |
.github/workflows/studio-frontend-build.yaml |
Uses packaged-build CI; critically, its write-enabled reusable workflow must be pinned to a commit SHA. |
.gitattributes |
Marks archives as binary; moderately, merge=ours is not portable without driver configuration. |
Review details
- Files reviewed: 26/34 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
@main is mutable and this caller grants contents: write, so a compromised or accidental upstream branch update could execute with write access to this repository. Pin to the current revision of reusable-studio-frontend-build-packaged.yaml, keeping main as an update comment. Co-Authored-By: Claude <noreply@anthropic.com>
merge=ours names a custom Git merge driver that isn't built in and isn't registered anywhere in this org (no repo configures merge.ours.driver), so the attribute was silently a no-op rather than actually preventing merge conflicts on the archive. Document the manual resolution instead: on a forward-merge conflict, keep the target branch's archive and let CI regenerate it on the next real source change. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Packaging can select a stale build directory and retain an outdated archive.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 26/34 changed files
- Comments generated: 2
- Review effort level: Balanced
studio-package-build selects the lexicographically greatest .build-id it finds on disk, not the one produced by the current run. Without cleanup, a directory left over from an earlier source state could sort higher than a fresh rebuild's id and get archived instead of it. This bundle has a single build target (no SDK/app pair sharing a build-id group like studio-ui-bundle itself has), so it's safe to remove every other build dir before building. Co-Authored-By: Claude <noreply@anthropic.com>
|



Summary
Adopts the same packaged frontend-build approach used by
studio-ui-bundle(2026.x) and pimcore-agent-bundle#92, as tracked in platform-version#247.Instead of committing the expanded
src/Resources/public/studio/build/tree, we now commit a single content-addressedbuild-dist/build-<id>.zipand letstudio-ui-bundle'sBuildArchiveExtractionTraitreconstructsrc/Resources/public/studio/build/at cache warmup. This removes the per-build file churn and merge conflicts the "Automatic frontend build" commits used to cause.What changed
WebpackEntryPointProvidernow implementsBuildArchiveProviderInterfacevia the sharedBuildArchiveExtractionTrait— no new logic, it just points atbuild-dist/build*.zip.rsbuild.config.tsusesstudio-ui-bundle'sgetBuildGroupId+pluginWriteBuildIdfor a deterministic, content-derived build id (identical source → identical archive), replacing the randomuuid-based id. The old "clean old builds" step is kept, but scoped to remove every dir except the current build id — needed becausestudio-package-buildpicks the lexicographically greatest.build-idit finds on disk, not the one just built, so a leftover stale dir could otherwise get archived instead.package.jsongained apackage-buildscript (studio-package-build) that packages the build intobuild-dist/.reusable-studio-frontend-build-packaged.yamlworkflow (pinned to a commit SHA, since it runs withcontents: write), watchingbuild-dist/**instead of the expanded build path..gitignoredrops the expanded tree;.gitattributesmarks the archivebinary+linguist-generated. It does not usemerge=ours— that names a custom Git merge driver that isn't registered anywhere in this org, so it would have been a silent no-op. A forward-merge conflict on the archive is instead resolved manually (keep the target branch's copy, e.g.git checkout --ours -- build-dist/); CI regenerates it on the next real source change. This is documented in the.gitattributescomment.composer.jsonalready requiredpimcore/studio-ui-bundle": "^2026.2.6", past the^2026.2.1minimum that ships theBuild/*classes — no version bump needed.Test plan
npm run buildfollowed bynpm run package-buildlocally against the newrsbuild.config.ts/package.json— producedbuild-dist/build-<id>.zipfrom a single build-id'd output directory as expected..build-id, ran the build, confirmed it was removed and the packaged archive matched the fresh build, not the stale one.npx tsc --noEmit— no new type errors introduced (pre-existing, unrelated antd-token errors only).php -l/ the PHP test suite locally (no PHP binary in this environment) — relying on CI (Codeception / static analysis) to validateWebpackEntryPointProvider.🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com