Skip to content

Ship the frontend build as a packaged archive - #40

Merged
ValeriaMaltseva merged 8 commits into
mainfrom
packaged-frontend-build
Sep 30, 2026
Merged

ValeriaMaltseva merged 8 commits into
mainfrom
packaged-frontend-build

Conversation

@ValeriaMaltseva

@ValeriaMaltseva ValeriaMaltseva commented Sep 22, 2026 •

Copy link
Copy Markdown

Resolves pimcore/platform-version#501.

Rolls out the packaged frontend-build mechanism (zip archive + warmup extraction) documented in
Shipping the Frontend Build as an Archive.
Until now this bundle committed the expanded public/build/ directory, so every build churned the
repository with newly hashed filenames and branches conflicted on the output. It now commits a single
build-dist/build-<id>.zip, which the Studio UI Bundle extracts into public/build/ during
bin/console cache:warmup.

Changes

File Change
assets/rsbuild.config.ts Build id comes from getBuildGroupId(__dirname) instead of a random uuid; adds pluginWriteBuildId so the id is written into the output as .build-id; the pre-build cleanup only sweeps stale build directories, so public/build/.gitkeep survives
assets/package.json Adds the package-build script (studio-package-build); @pimcore/studio-ui-bundle 2026.2.0 → ^2026.2.11; drops the now unused uuid dependency
assets/package-lock.json Lock update for the above
src/Webpack/WebpackEntryPointProvider.php Implements BuildArchiveProviderInterface via BuildArchiveExtractionTrait
.gitignore Ignores /public/build/*, keeps /public/build/.gitkeep
public/build/.gitkeep Keeps public/build/ in git so it exists after composer install for assets:install to link
build-dist/build-fc755267691a.zip Replaces the 16 committed files under public/build/
.github/workflows/studio-frontend-build.yml Thin caller of reusable-studio-frontend-build-packaged.yaml with working-directory: assets
.github/workflows/studio-frontend-build.yaml Removed (superseded inline workflow, see below)
composer.json pimcore/studio-ui-bundle → ^2025.4.9 || ^2026.2.1
README.md Documents the build flow

Workflows

The repository had two workflows, both named "Studio Frontend Build":

  • studio-frontend-build.yml called the non-packaged reusable-studio-frontend-build.yaml but watched
    assets/studio/**, a path that does not exist in this repository, so it never triggered.
  • studio-frontend-build.yaml was the old inline lint/check-types/build pipeline that committed the
    expanded ./public/ output.

studio-frontend-build.yml now calls the packaged reusable workflow with working-directory: assets,
watches assets/** (plus build-dist/** on push) and is renamed to "Studio Frontend Build Packaged".
Lint (lint-fix) and type checks (check-types) run inside the reusable workflow. It executes project
code in read-only jobs without persisted git credentials and keeps the contents: write token in commit
jobs that run no project code.

studio-frontend-build.yaml is removed. It could not work any more: its jobs rely on an
actions/cache of ./assets/node_modules saved by a separate install job, but the token has no
writable cache scope, so the save is denied and lint / check-types start with nothing installed.
It also used pull_request_target while checking out the pull request head, which runs fork code with
a write-scoped token.

Composer constraint drops the 2026.1 line

The archive mechanism is only available in studio-ui-bundle 2025.4.9+ and 2026.2.1+, so
^2025.4 || ^2026.1 became ^2025.4.9 || ^2026.2.1. studio-backend-bundle and
studio-dashboards-bundle are left at ^2025.4 || ^2026.1 — pre-existing and out of scope here, but
they are now inconsistent with the ui constraint and probably want the same treatment.

Verification

"Studio Frontend Build Packaged" is green on the branch head: lint, check-types, build and
commit-build all pass. The meaningful result is what commit-build did not do: CI rebuilt from
source on a clean runner, derived the same build id, produced the same archive and had nothing to
commit, leaving the branch head unchanged. That is the no-churn property the change exists for,
confirmed across machines rather than only locally.

Also checked locally in a clean clone:

  • npm run check-types passes; npm run lint-fix produces no diff
  • npm run build && npm run package-build produces build-dist/build-<id>.zip; repeating both
    leaves it byte-identical (already up to date for this source; nothing to do)
  • Extracting the archive into public/build/ reproduces exactly the */entrypoints.json glob the
    extractor uses, with .build-id present and the declared exposeRemote entry point in place
  • The compiled entrypoints.json URLs match the assetPrefix
    (/bundles/pimcorestudioexample/build/<id>/...)

Not verified locally: no PHP lint or static analysis was run (no PHP available on the machine). The
provider was checked against the upstream BuildArchiveExtractionTrait / BuildArchiveProviderInterface
signatures and mirrors the equivalent one in studio-dashboards-bundle.

Note on the red lint / check-types checks

These come from studio-frontend-build.yaml, not from this branch. It triggers on
pull_request_target, so GitHub runs the copy on main (where the file still exists) against this PR,
and it fails for the reason above (eslint: not found, and the runner's global tsc rejecting
baseUrl without @types/webpack-env). The same checks pass in "Studio Frontend Build Packaged".
The red checks disappear once this merges and the file is gone from main.

Follow-up

doc/04_Extending/04_Shipping_the_Frontend_Build_as_an_Archive.md in studio-ui-bundle states
"Commit the expanded build directory (public/build/). This is what the Studio Example Bundle does",
which stops being true once this merges. Handled in pimcore/studio-ui-bundle#4124.

🤖 Generated with Claude Code

Replace the committed public/build/ directory with a single committed
archive, build-dist/build-<id>.zip, which the Studio UI Bundle extracts
into public/build/ during cache warmup.

- rsbuild.config.ts derives the build id from the contents of assets/
  via getBuildGroupId() instead of a random uuid, and writes it into the
  output with pluginWriteBuildId(). Unchanged source now yields the same
  id, so a rebuild produces no diff.
- Add the package-build script (studio-package-build) and drop the now
  unused uuid dependency.
- WebpackEntryPointProvider implements BuildArchiveProviderInterface via
  BuildArchiveExtractionTrait.
- Gitignore public/build/ and commit build-dist/ instead.
- Collapse the two build workflows that were both active into a single
  thin caller of reusable-studio-frontend-build-packaged.yaml. The old
  hand-rolled workflow committed the expanded ./public/ output, and the
  parked reusable caller watched assets/studio/** which does not exist
  in this repo.

The archive mechanism needs studio-ui-bundle 2025.4.9+ or 2026.2.1+, so
the composer constraint and the npm dependency are raised accordingly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ValeriaMaltseva and others added 5 commits September 22, 2026 11:35
…tead

Restore .github/workflows/studio-frontend-build.yaml to its state on main
and move the packaged build over to studio-frontend-build.yml, which now
calls reusable-studio-frontend-build-packaged.yaml with
working-directory: assets and watches assets/** instead of the
assets/studio/** path that does not exist in this repository.

Renamed to "Studio Frontend Build Packaged" so the two workflows are
distinguishable in the Actions UI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removed comments explaining the build process.
Gitignoring the expanded build left no tracked file under public/, so the
directory would not exist after a fresh composer install and there would
be nothing for assets:install to link. Track public/build/.gitkeep and
negate it in .gitignore, following studio-ui-bundle.

The pre-build cleanup in rsbuild.config.ts swept every entry in
public/build/, which deleted that .gitkeep on each build. Restrict it to
directories; plain files at that level are left alone. pluginWriteBuildId
already skips non-directories.

Remove the 43 "peer": true markers that the dependency update added to
assets/package-lock.json, keeping the 2 that were already there. They are
install metadata and do not affect npm ci resolution.

The build id is a hash of assets/, so the lockfile and config edits change
it: the archive is rebuilt as build-fc755267691a.zip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
studio-frontend-build.yaml was the old inline lint/check-types/build
pipeline. It was superseded by studio-frontend-build.yml, which delegates
to the reusable workflow, but the file was never removed, so both ran
under the same name "Studio Frontend Build".

The leftover one could not work any more: its jobs inherit the
workflow-level `contents: read` permission, so the install job's
actions/cache step runs in read mode ("cache write denied: token has no
writable scopes") and never saves ./assets/node_modules. The dependent
lint and check-types jobs then start on a cache miss with no
node_modules, which is why lint reports "eslint: not found" and
check-types falls through to the runner's global tsc (a newer major that
removed baseUrl) without @types/webpack-env installed.

It also used pull_request_target while checking out the pull request
head, which runs fork code with a write-scoped token.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carries the two jobs over from the removed studio-frontend-build.yaml so
the repository keeps its own lint-fix and type-check steps, and turns the
reusable workflow's equivalents off so neither runs twice and only one
job pushes to the branch.

The jobs install their own dependencies instead of depending on a shared
install job that caches ./assets/node_modules. That cache was the reason
both jobs failed: the cache token is not writable here, so the save was
denied and the restore missed, leaving the jobs with nothing installed.
setup-node's npm cache is kept, since it caches the download cache rather
than the installed tree and needs no cross-job save.

The build is ordered after both jobs, matching the removed workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The caller's own lint and check-types jobs ran npm ci and repo scripts
with a contents: write token and persisted git credentials, so any npm
install hook could push to the repository. The reusable packaged workflow
already runs the same lint-fix and check-types scripts, but in read-only
jobs without git credentials, and keeps the write token in commit jobs
that execute no project code. It also checks out fork PRs via
refs/pull/<n>/head, which works for private forks.

Drop the duplicated jobs and go back to a thin caller.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@berfinyuksel berfinyuksel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workflow needs to be checked

berfinyuksel added a commit that referenced this pull request Sep 30, 2026
The install job saves node_modules to actions/cache for the lint and
check-types jobs. Since GitHub's read-only cache for untrusted triggers
(2026-06-26), pull_request_target runs cannot save, so those jobs start
with nothing installed and fail on every PR. The reusable packaged
workflow in studio-frontend-build.yml replaces it (see #40).
@berfinyuksel
berfinyuksel self-requested a review September 30, 2026 15:18

@berfinyuksel berfinyuksel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after rebase tests will turn green you can continue merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ValeriaMaltseva
ValeriaMaltseva merged commit e7738fd into main Sep 30, 2026
6 checks passed
@ValeriaMaltseva
ValeriaMaltseva deleted the packaged-frontend-build branch September 30, 2026 15:31
ValeriaMaltseva added a commit to pimcore/studio-ui-bundle that referenced this pull request Sep 30, 2026
pimcore/studio-example-bundle#40 added the build-id logic above the entry
config, moving `main: './js/src/main.ts'` from line 49 to line 59.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ValeriaMaltseva added a commit to pimcore/studio-ui-bundle that referenced this pull request Sep 30, 2026
…4124)

* [Docs] The Studio Example Bundle now ships its build as an archive

The Example Bundle moved from a committed public/build/ directory to a
committed build-dist/build-<id>.zip (pimcore/studio-example-bundle#40),
so it is no longer an example of the "commit the expanded build
directory" option, and the provider linked from Registering the Frontend
Build is no longer a plain provider.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* [Docs] Point the rsbuild main.ts link at its new line

pimcore/studio-example-bundle#40 added the build-id logic above the entry
config, moving `main: './js/src/main.ts'` from line 49 to line 59.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Studio Example Bundle] Roll out the packaged frontend-build (zip archive + warmup extraction)Horizon

2 participants